Skip to content

Commit 700397f

Browse files
committed
add XLIFF key generation support #479
1 parent 8747f12 commit 700397f

File tree

7 files changed

+277
-32
lines changed

7 files changed

+277
-32
lines changed

src/fr/adrienbrault/idea/symfony2plugin/action/TwigExtractLanguageAction.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import com.intellij.codeInsight.lookup.LookupElement;
44
import com.intellij.openapi.actionSystem.AnActionEvent;
55
import com.intellij.openapi.actionSystem.PlatformDataKeys;
6+
import com.intellij.openapi.application.ApplicationManager;
67
import com.intellij.openapi.application.Result;
8+
import com.intellij.openapi.command.CommandProcessor;
79
import com.intellij.openapi.command.WriteCommandAction;
810
import com.intellij.openapi.editor.Editor;
911
import com.intellij.openapi.project.DumbAwareAction;
@@ -14,6 +16,7 @@
1416
import com.intellij.psi.PsiFile;
1517
import com.intellij.psi.tree.IElementType;
1618
import com.intellij.psi.util.PsiTreeUtil;
19+
import com.intellij.psi.xml.XmlFile;
1720
import com.intellij.psi.xml.XmlTokenType;
1821
import com.jetbrains.twig.TwigFile;
1922
import com.jetbrains.twig.TwigTokenTypes;
@@ -28,6 +31,7 @@
2831
import fr.adrienbrault.idea.symfony2plugin.translation.util.TranslationInsertUtil;
2932
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
3033
import org.apache.commons.lang.StringUtils;
34+
import org.jetbrains.annotations.NotNull;
3135
import org.jetbrains.yaml.psi.YAMLFile;
3236

3337
import java.awt.*;
@@ -231,7 +235,7 @@ public void onClick(List<TranslationFileModel> files, final String keyName, fina
231235

232236
new WriteCommandAction(project) {
233237
@Override
234-
protected void run(Result result) throws Throwable {
238+
protected void run(@NotNull Result result) throws Throwable {
235239
String insertString;
236240

237241
// check for file context domain
@@ -253,10 +257,19 @@ public String getGroupID() {
253257

254258
// so finally insert it; first file can be a navigation target
255259
for(TranslationFileModel transPsiFile: files) {
256-
TranslationInsertUtil.invokeTranslation(editor, keyName, finalTranslationText, (YAMLFile) transPsiFile.getPsiFile(), navigateTo);
260+
PsiFile psiFile = transPsiFile.getPsiFile();
261+
if(psiFile instanceof YAMLFile) {
262+
TranslationInsertUtil.invokeTranslation(editor, keyName, finalTranslationText, (YAMLFile) psiFile, navigateTo);
263+
} else if(TranslationUtil.isSupportedXlfFile(psiFile)) {
264+
CommandProcessor.getInstance().executeCommand(psiFile.getProject(), () -> ApplicationManager.getApplication().runWriteAction(() ->
265+
ApplicationManager.getApplication().runWriteAction(() ->
266+
TranslationInsertUtil.insertTranslation((XmlFile) psiFile, keyName, finalTranslationText)
267+
)
268+
), null, null);
269+
}
270+
257271
navigateTo = false;
258272
}
259-
260273
}
261274
}
262275
}

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigAnnotator.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.intellij.patterns.PlatformPatterns;
77
import com.intellij.psi.PsiElement;
88
import com.intellij.psi.PsiFile;
9+
import com.intellij.psi.xml.XmlFile;
910
import com.jetbrains.twig.TwigTokenTypes;
1011
import fr.adrienbrault.idea.symfony2plugin.Settings;
1112
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
@@ -77,9 +78,9 @@ private void annotateTranslationKey(@NotNull final PsiElement psiElement, @NotNu
7778
if(!TranslationUtil.hasTranslationKey(psiElement.getProject(), text, domainName)) {
7879
Annotation annotationHolder = holder.createWarningAnnotation(psiElement, "Missing Translation");
7980
List<PsiFile> psiElements = TranslationUtil.getDomainPsiFiles(psiElement.getProject(), domainName);
80-
for(PsiElement psiFile: psiElements) {
81-
if(psiFile instanceof YAMLFile) {
82-
annotationHolder.registerFix(new TranslationKeyIntentionAction((YAMLFile) psiFile, text));
81+
for(PsiFile psiFile: psiElements) {
82+
if(psiFile instanceof YAMLFile || TranslationUtil.isSupportedXlfFile(psiFile)) {
83+
annotationHolder.registerFix(new TranslationKeyIntentionAction(psiFile, text));
8384
}
8485
}
8586
}

src/fr/adrienbrault/idea/symfony2plugin/translation/TranslationKeyIntentionAction.java

+23-17
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,45 @@
22

33
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
44
import com.intellij.openapi.application.ApplicationManager;
5+
import com.intellij.openapi.command.CommandProcessor;
56
import com.intellij.openapi.editor.Editor;
67
import com.intellij.openapi.project.Project;
78
import com.intellij.openapi.vfs.VfsUtil;
89
import com.intellij.openapi.vfs.VirtualFile;
910
import com.intellij.psi.PsiFile;
1011
import com.intellij.psi.PsiManager;
12+
import com.intellij.psi.xml.XmlFile;
1113
import com.intellij.util.IncorrectOperationException;
1214
import fr.adrienbrault.idea.symfony2plugin.translation.util.TranslationInsertUtil;
1315
import org.jetbrains.annotations.NotNull;
1416
import org.jetbrains.yaml.psi.YAMLFile;
1517

1618
public class TranslationKeyIntentionAction extends BaseIntentionAction {
19+
@NotNull
20+
private PsiFile psiFile;
1721

18-
protected YAMLFile yamlFile;
19-
protected String keyName;
22+
@NotNull
23+
private String keyName;
2024

2125
/**
22-
*
23-
* @param yamlFile Translation file as yaml
26+
* @param psiFile Translation file as yaml
2427
* @param keyName key name like "translation" or "translation.sub.name"
2528
*/
26-
public TranslationKeyIntentionAction(YAMLFile yamlFile, String keyName) {
27-
this.yamlFile = yamlFile;
29+
public TranslationKeyIntentionAction(@NotNull PsiFile psiFile, @NotNull String keyName) {
30+
this.psiFile = psiFile;
2831
this.keyName = keyName;
2932
}
3033

3134
@NotNull
3235
@Override
3336
public String getText() {
34-
String filename = yamlFile.getName();
37+
String filename = psiFile.getName();
3538

3639
// try to find suitable presentable filename
37-
VirtualFile virtualFile = yamlFile.getVirtualFile();
40+
VirtualFile virtualFile = psiFile.getVirtualFile();
3841
if(virtualFile != null) {
3942
filename = virtualFile.getPath();
40-
String relativePath = VfsUtil.getRelativePath(virtualFile, yamlFile.getProject().getBaseDir(), '/');
43+
String relativePath = VfsUtil.getRelativePath(virtualFile, psiFile.getProject().getBaseDir(), '/');
4144
if(relativePath != null) {
4245
filename = relativePath;
4346
}
@@ -59,20 +62,23 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file
5962

6063
@Override
6164
public void invoke(@NotNull Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
62-
63-
VirtualFile virtualFile = TranslationKeyIntentionAction.this.yamlFile.getVirtualFile();
65+
VirtualFile virtualFile = TranslationKeyIntentionAction.this.psiFile.getVirtualFile();
6466
if(virtualFile == null) {
6567
return;
6668
}
6769

6870
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
69-
if(!(psiFile instanceof YAMLFile)) {
70-
return;
71+
if(psiFile instanceof YAMLFile) {
72+
ApplicationManager.getApplication().runWriteAction(() -> {
73+
TranslationInsertUtil.invokeTranslation(editor, keyName, keyName, (YAMLFile) psiFile, true);
74+
});
75+
} else if(psiFile instanceof XmlFile) {
76+
CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() ->
77+
ApplicationManager.getApplication().runWriteAction(() ->
78+
TranslationInsertUtil.insertTranslation((XmlFile) psiFile, keyName, keyName)
79+
)
80+
), null, null);
7181
}
72-
73-
ApplicationManager.getApplication().runWriteAction(() -> {
74-
TranslationInsertUtil.invokeTranslation(editor, keyName, keyName, (YAMLFile) psiFile, true);
75-
});
7682
}
7783

7884
}

src/fr/adrienbrault/idea/symfony2plugin/translation/dict/TranslationUtil.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,16 @@ public static PsiElement[] getTranslationPsiElements(final Project project, fina
118118
}
119119

120120
PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
121+
if(psiFile == null) {
122+
return true;
123+
}
124+
121125
if(psiFile instanceof YAMLFile) {
122126
YamlTranslationVistor.collectFileTranslations((YAMLFile) psiFile, translationCollector);
123-
} else if(("xlf".equalsIgnoreCase(virtualFile.getExtension()) || "xliff".equalsIgnoreCase(virtualFile.getExtension())) && psiFile instanceof XmlFile) {
127+
} else if(isSupportedXlfFile(psiFile)) {
124128
// fine: xlf registered as XML file. try to find source value
125129
psiFoundElements.addAll(getTargetForXlfAsXmlFile((XmlFile) psiFile, translationKey));
126-
} else if(("xlf".equalsIgnoreCase(virtualFile.getExtension()) || "xliff".equalsIgnoreCase(virtualFile.getExtension()) && psiFile != null)) {
130+
} else if(("xlf".equalsIgnoreCase(virtualFile.getExtension()) || "xliff".equalsIgnoreCase(virtualFile.getExtension()))) {
127131
// xlf are plain text because not supported by jetbrains
128132
// for now we can only set file target
129133
psiFoundElements.addAll(
@@ -300,7 +304,7 @@ public static List<PsiFile> getDomainPsiFiles(final Project project, String doma
300304
}
301305
}
302306

303-
FileBasedIndexImpl.getInstance().getFilesWithKey(YamlTranslationStubIndex.KEY, new HashSet<>(Arrays.asList(domainName)), virtualFile -> {
307+
FileBasedIndexImpl.getInstance().getFilesWithKey(YamlTranslationStubIndex.KEY, new HashSet<>(Collections.singletonList(domainName)), virtualFile -> {
304308
if(uniqueFileList.contains(virtualFile)) {
305309
return true;
306310
}
@@ -346,6 +350,15 @@ public static Set<String> getXliffTranslations(InputStream content) {
346350
return set;
347351
}
348352

353+
public static boolean isSupportedXlfFile(@NotNull PsiFile psiFile) {
354+
if(!(psiFile instanceof XmlFile)) {
355+
return false;
356+
}
357+
358+
String extension = psiFile.getVirtualFile().getExtension();
359+
return "xlf".equalsIgnoreCase(extension) || "xliff".equalsIgnoreCase(extension);
360+
}
361+
349362
private static void visitNodes(@NotNull String xpath, @NotNull Set<String> set, @NotNull Document document) {
350363
Object result;
351364
try {

src/fr/adrienbrault/idea/symfony2plugin/translation/form/TranslatorKeyExtractorDialog.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.intellij.openapi.project.Project;
44
import com.intellij.psi.PsiFile;
5+
import com.intellij.psi.xml.XmlFile;
56
import com.intellij.ui.ToolbarDecorator;
67
import com.intellij.ui.table.TableView;
78
import com.intellij.util.ui.ColumnInfo;
@@ -26,6 +27,7 @@
2627
import java.util.Collection;
2728
import java.util.Collections;
2829
import java.util.List;
30+
import java.util.stream.Collectors;
2931

3032
public class TranslatorKeyExtractorDialog extends JDialog {
3133

@@ -116,12 +118,9 @@ private void filterList(String domainName) {
116118

117119
// we only support yaml files right now
118120
// filter on PsiFile instance
119-
Collection<PsiFile> domainPsiFilesYaml = new ArrayList<>();
120-
for(PsiFile domainPsiFiles: TranslationUtil.getDomainPsiFiles(this.project, domainName)) {
121-
if(domainPsiFiles instanceof YAMLFile) {
122-
domainPsiFilesYaml.add(domainPsiFiles);
123-
}
124-
}
121+
Collection<PsiFile> domainPsiFilesYaml = TranslationUtil.getDomainPsiFiles(this.project, domainName).stream()
122+
.filter(domainPsiFile -> domainPsiFile instanceof YAMLFile || TranslationUtil.isSupportedXlfFile(domainPsiFile))
123+
.collect(Collectors.toCollection(ArrayList::new));
125124

126125
this.listTableModel.addRows(this.getFormattedFileModelList(domainPsiFilesYaml));
127126

src/fr/adrienbrault/idea/symfony2plugin/translation/util/TranslationInsertUtil.java

+101
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
import com.intellij.psi.PsiDocumentManager;
88
import com.intellij.psi.PsiElement;
99
import com.intellij.psi.PsiFile;
10+
import com.intellij.psi.XmlElementFactory;
1011
import com.intellij.psi.util.PsiTreeUtil;
12+
import com.intellij.psi.xml.XmlFile;
13+
import com.intellij.psi.xml.XmlTag;
14+
import com.intellij.util.Consumer;
1115
import fr.adrienbrault.idea.symfony2plugin.util.IdeHelper;
1216
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
1317
import org.jetbrains.annotations.NotNull;
@@ -38,6 +42,103 @@ public static void invokeTranslation(@NotNull final Editor editor, @NotNull fina
3842
});
3943
}
4044

45+
public static void insertTranslation(@NotNull final XmlFile xmlFile, @NotNull final String keyName, @NotNull final String translation) {
46+
XmlTag rootTag = xmlFile.getRootTag();
47+
if(rootTag == null) {
48+
return;
49+
}
50+
51+
String version = rootTag.getAttributeValue("version");
52+
if(version == null) {
53+
return;
54+
}
55+
56+
XmlTag file = rootTag.findFirstSubTag("file");
57+
if(file == null) {
58+
return;
59+
}
60+
61+
// version="1.2"
62+
if(version.equalsIgnoreCase("1.2")) {
63+
Consumer<XmlTag> consumer12 = body -> {
64+
XmlElementFactory instance = XmlElementFactory.getInstance(xmlFile.getProject());
65+
66+
XmlTag source = instance.createTagFromText("<source/>");
67+
source.getValue().setText(keyName);
68+
69+
XmlTag target = instance.createTagFromText("<target/>");
70+
target.getValue().setText(translation);
71+
72+
XmlTag transUnit = instance.createTagFromText("<trans-unit/>");
73+
transUnit.setAttribute("id", String.valueOf(getIdForNewXlfUnit(body, "trans-unit")));
74+
75+
transUnit.addSubTag(source, false);
76+
transUnit.addSubTag(target, false);
77+
78+
body.addSubTag(transUnit, false);
79+
};
80+
81+
XmlTag body = file.findFirstSubTag("body");
82+
if(body != null) {
83+
consumer12.consume(body);
84+
}
85+
} else if(version.equalsIgnoreCase("2.0")) {
86+
Consumer<XmlTag> consumer20 = body -> {
87+
XmlElementFactory instance = XmlElementFactory.getInstance(xmlFile.getProject());
88+
89+
XmlTag source = instance.createTagFromText("<source/>");
90+
source.getValue().setText(keyName);
91+
92+
XmlTag target = instance.createTagFromText("<target/>");
93+
target.getValue().setText(translation);
94+
95+
XmlTag transUnit = instance.createTagFromText("<unit/>");
96+
transUnit.setAttribute("id", String.valueOf(String.valueOf(getIdForNewXlfUnit(body, "unit"))));
97+
98+
XmlTag segment = transUnit.addSubTag(instance.createTagFromText("<segment/>"), false);
99+
100+
segment.addSubTag(source, false);
101+
segment.addSubTag(target, false);
102+
103+
body.addSubTag(transUnit, false);
104+
};
105+
106+
// version="2.0"
107+
XmlTag group = file.findFirstSubTag("group");
108+
if(group != null) {
109+
consumer20.consume(group);
110+
} else {
111+
// version="2.0" shortcut
112+
consumer20.consume(file);
113+
}
114+
}
115+
}
116+
117+
private static int getIdForNewXlfUnit(@NotNull XmlTag body, @NotNull String subTag) {
118+
int lastId = 0;
119+
120+
for (XmlTag transUnit : body.findSubTags(subTag)) {
121+
String id = transUnit.getAttributeValue("id");
122+
if(id == null) {
123+
continue;
124+
}
125+
126+
Integer integer;
127+
try {
128+
integer = Integer.valueOf(id);
129+
} catch (NumberFormatException e) {
130+
continue;
131+
}
132+
133+
// next safe id
134+
if(integer > lastId) {
135+
lastId = integer + 1;
136+
}
137+
}
138+
139+
return lastId;
140+
}
141+
41142
/**
42143
* Remove TODO; moved to core
43144
*/

0 commit comments

Comments
 (0)