diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java index 543df9e..01c9a12 100644 --- a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java +++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/GherkinCheckVerifier.java @@ -56,18 +56,17 @@ public class GherkinCheckVerifier extends SubscriptionVisitorCheck { /** * Check issues + * Example: + *
+   * GherkinCheckVerifier.issues(new MyCheck(), myFile)
+   *   .next().atLine(2).withMessage("This is message for line 2.")
+   *   .next().atLine(3).withMessage("This is message for line 3.").withCost(2.)
+   *   .next().atLine(8)
+   *   .noMore();
+   * 
* * @param check Check to test * @param file File to test - *

- * Example: - *

-   *                             GherkinCheckVerifier.issues(new MyCheck(), myFile)
-   *                               .next().atLine(2).withMessage("This is message for line 2.")
-   *                               .next().atLine(3).withMessage("This is message for line 3.").withCost(2.)
-   *                               .next().atLine(8)
-   *                               .noMore();
-   *                           
*/ public static CheckMessagesVerifier issues(GherkinCheck check, File file) { return issues(check, file, Charsets.UTF_8); @@ -123,7 +122,7 @@ public static void verify(GherkinCheck check, File file) { * @param charset Charset of the file to test. */ public static void verify(GherkinCheck check, File file, Charset charset) { - GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createParser(charset).parse(file); + GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset).parse(file); GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file); GherkinCheckVerifier checkVerifier = new GherkinCheckVerifier(); diff --git a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java index 9f98f42..b7a2fe0 100644 --- a/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java +++ b/gherkin-checks-testkit/src/main/java/org/sonar/gherkin/checks/verifier/TreeCheckTest.java @@ -43,7 +43,7 @@ private TreeCheckTest() { public static Collection getIssues(String relativePath, GherkinCheck check, Charset charset) { File file = new File(relativePath); - GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createParser(charset).parse(file); + GherkinDocumentTree propertiesTree = (GherkinDocumentTree) GherkinParserBuilder.createTestParser(charset).parse(file); GherkinVisitorContext context = new GherkinVisitorContext(propertiesTree, file); List issues = check.scanFile(context); diff --git a/gherkin-checks/pom.xml b/gherkin-checks/pom.xml index b722715..f2ddecd 100644 --- a/gherkin-checks/pom.xml +++ b/gherkin-checks/pom.xml @@ -39,7 +39,7 @@ org.languagetool - language-en + language-all junit diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AddCommonGivenStepsToBackgroundCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AddCommonGivenStepsToBackgroundCheck.java index 3ea649b..322a958 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AddCommonGivenStepsToBackgroundCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AddCommonGivenStepsToBackgroundCheck.java @@ -78,10 +78,10 @@ private int findLastCommonGivenStepRank() { search: for (int i = 0; i < allSteps.get(0).size(); i++) { - if (allSteps.get(0).get(i).type() == StepTree.StepType.GIVEN) { + if (allSteps.get(0).get(i).semanticType() == StepTree.SemanticStepType.GIVEN) { for (int j = 1; j < allSteps.size(); j++) { if (allSteps.get(j).size() <= i - || allSteps.get(j).get(i).type() != StepTree.StepType.GIVEN + || allSteps.get(j).get(i).semanticType() != StepTree.SemanticStepType.GIVEN || !allSteps.get(j).get(i).sentence().text().equals(allSteps.get(0).get(i).sentence().text())) { break search; } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheck.java index efd74c0..2b807b3 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheck.java @@ -70,7 +70,7 @@ private void checkAllStepTypes(BasicScenarioTree tree) { int thens = 0; for (StepTree step : Stream.concat(backgroundSteps.stream(), tree.steps().stream()).collect(Collectors.toList())) { - switch (step.type()) { + switch (step.semanticType()) { case GIVEN: givens++; break; diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/GivenStepRegularExpressionCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/GivenStepRegularExpressionCheck.java index 91ad3f2..eab237f 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/GivenStepRegularExpressionCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/GivenStepRegularExpressionCheck.java @@ -47,7 +47,7 @@ public class GivenStepRegularExpressionCheck extends DoubleDispatchVisitorCheck @Override public void visitStep(StepTree tree) { - if (tree.type() == StepTree.StepType.GIVEN + if (tree.semanticType() == StepTree.SemanticStepType.GIVEN && !tree.sentence().text().matches(regularExpression)) { addPreciseIssue(tree.sentence(), "Update the sentence to match the following regular expression: " + regularExpression); } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/IndentationCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/IndentationCheck.java index 22c34dc..e6bdb6d 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/IndentationCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/IndentationCheck.java @@ -26,6 +26,7 @@ import org.sonar.check.RuleProperty; import org.sonar.plugins.gherkin.api.tree.*; import org.sonar.plugins.gherkin.api.visitors.SubscriptionVisitorCheck; +import org.sonar.plugins.gherkin.api.visitors.issue.PreciseIssue; import org.sonar.squidbridge.annotations.ActivatedByDefault; import org.sonar.squidbridge.annotations.SqaleConstantRemediation; @@ -57,7 +58,6 @@ public List nodesToVisit() { Tree.Kind.SCENARIO, Tree.Kind.SCENARIO_OUTLINE, Tree.Kind.EXAMPLES, - Tree.Kind.STEP, Tree.Kind.FEATURE_PREFIX, Tree.Kind.BACKGROUND_PREFIX, Tree.Kind.SCENARIO_PREFIX, @@ -65,8 +65,7 @@ public List nodesToVisit() { Tree.Kind.EXAMPLES_PREFIX, Tree.Kind.STEP_PREFIX, Tree.Kind.TABLE, - Tree.Kind.DOC_STRING, - Tree.Kind.NAME + Tree.Kind.DOC_STRING ); } @@ -80,15 +79,18 @@ public void visitNode(Tree tree) { checkAllTagsIndentation(tree); } + if (tree instanceof Prefixable && tree instanceof Nameable) { + checkAllNamesIndentation(tree); + } + if (tree instanceof PrefixTree) { checkAllPrefixesIndentation((PrefixTree) tree); } else if (tree.is(Tree.Kind.DOC_STRING)) { - checkDocStringsIndentation((DocStringTree) tree); + checkAllDocStringsIndentation((DocStringTree) tree); } else if (tree.is(Tree.Kind.TABLE)) { checkAllTablesIndentation((TableTree) tree); - } else if (tree.is(Tree.Kind.NAME)) { - checkNameIndentation((NameTree) tree); } + } @VisibleForTesting @@ -192,51 +194,19 @@ private void checkAllTablesIndentation(TableTree tree) { tree.rows().forEach(d -> checkIndentation(d, indentation * 3)); } - private void checkDocStringsIndentation(DocStringTree tree) { + private void checkAllDocStringsIndentation(DocStringTree tree) { checkIndentation(tree.prefix(), indentation * 3); checkIndentation(tree.suffix(), indentation * 3); } - private void checkNameIndentation(NameTree tree) { - int expectedIndentation = -1; - - switch (tree.parent().getKind()) { - case FEATURE_DECLARATION: - if (((FeatureDeclarationTree) tree.parent()).prefix().keyword().column() == 0) { - expectedIndentation = 9; - } - break; - - case BACKGROUND: - if (((BackgroundTree) tree.parent()).prefix().keyword().column() == indentation) { - expectedIndentation = indentation + 12; - } - break; - - case SCENARIO: - if (((ScenarioTree) tree.parent()).prefix().keyword().column() == indentation) { - expectedIndentation = indentation + 10; - } - break; - - case SCENARIO_OUTLINE: - if (((ScenarioOutlineTree) tree.parent()).prefix().keyword().column() == indentation) { - expectedIndentation = indentation + 18; - } - break; - - case EXAMPLES: - if (((ExamplesTree) tree.parent()).prefix().keyword().column() == indentation * 2) { - expectedIndentation = indentation * 2 + 10; - } - break; - - default: - throw new IllegalStateException("Unsupported NameTree: " + tree.toString()); - } + private void checkAllNamesIndentation(Tree tree) { + NameTree nameTree = ((Nameable) tree).name(); + SyntaxToken columnTree = ((Prefixable) tree).colon(); - if (expectedIndentation >= 0) { - checkIndentation(tree.value(), expectedIndentation); + if (nameTree != null + && columnTree.endColumn() + 1 != nameTree.value().column()) { + PreciseIssue issue = addPreciseIssue(nameTree, "Leave one single whitespace between the name and the column."); + issue.secondary(columnTree, ""); } } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OneSingleWhenPerScenarioCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OneSingleWhenPerScenarioCheck.java index 4d8a3aa..d9a8106 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OneSingleWhenPerScenarioCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OneSingleWhenPerScenarioCheck.java @@ -51,7 +51,7 @@ public List nodesToVisit() { public void visitNode(Tree tree) { List whenSteps = ((BasicScenarioTree) tree).steps() .stream() - .filter(s -> s.type() == StepTree.StepType.WHEN) + .filter(s -> s.semanticType() == StepTree.SemanticStepType.WHEN) .collect(Collectors.toList()); if (whenSteps.size() > 1) { diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OnlyGivenStepsInBackgroundCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OnlyGivenStepsInBackgroundCheck.java index 3a10085..7027daa 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OnlyGivenStepsInBackgroundCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/OnlyGivenStepsInBackgroundCheck.java @@ -40,7 +40,7 @@ public class OnlyGivenStepsInBackgroundCheck extends DoubleDispatchVisitorCheck public void visitBackground(BackgroundTree tree) { tree.steps() .stream() - .filter(s -> s.type() != StepTree.StepType.GIVEN) + .filter(s -> s.semanticType() != StepTree.SemanticStepType.GIVEN) .forEach(s -> addPreciseIssue(s, "Move this non-Given step out of Background.")); super.visitBackground(tree); diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/SpellingCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/SpellingCheck.java index a118a00..31baf58 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/SpellingCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/SpellingCheck.java @@ -23,9 +23,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.languagetool.JLanguageTool; -import org.languagetool.language.AmericanEnglish; -import org.languagetool.language.BritishEnglish; -import org.languagetool.language.English; +import org.languagetool.Languages; import org.languagetool.rules.spelling.SpellingCheckRule; import org.sonar.check.Priority; import org.sonar.check.Rule; @@ -53,11 +51,18 @@ public class SpellingCheck extends SubscriptionVisitorCheck { private static final String DEFAULT_WORDS_TO_IGNORE = ""; private static final String DEFAULT_RULES_TO_IGNORE = "EN_QUOTES"; - private static final String EN_US_LANGUAGE = "en_US"; - private static final String EN_GB_LANGUAGE = "en_GB"; + private static final String DEFAULT_LANGUAGE = "en-US"; + private static final Set SUPPORTED_LANGUAGES = ImmutableSet.of("ast-ES", "be-BY", "br-FR", "ca-ES", + "ca-ES-valencia", "da-DK", "de", "de-AT", "de-CH", "de-DE", "de-DE-x-simple-language", "el-GR", "en", "en-AU", + "en-CA", "en-GB", "en-NZ", "en-US", "en-ZA", "eo", "es", "fa", "fr", "gl-ES", "is-IS", "it", "ja-JP", "km-KH", + "lt-LT", "ml-IN", "nl", "pl-PL", "pt", "pt-BR", "pt-PT", "ro-RO", "ru-RU", "sk-SK", "sl-SI", "sv", "ta-IN", + "tl-PH", "uk-UA", "zh-CN"); + + private static final String SUPPORTED_LANGUAGES_AS_STRING = "ast-ES, be-BY, br-FR, ca-ES, ca-ES-valencia, da-DK, " + + "de, de-AT, de-CH, de-DE, de-DE-x-simple-language, el-GR, en, en-AU, en-CA, en-GB, en-NZ, en-US, en-ZA, eo, " + + "es, fa, fr, gl-ES, is-IS, it, ja-JP, km-KH, lt-LT, ml-IN, nl, pl-PL, pt, pt-BR, pt-PT, ro-RO, ru-RU, sk-SK, " + + "sl-SI, sv, ta-IN, tl-PH, uk-UA, zh-CN"; - private static final String DEFAULT_LANGUAGE = EN_US_LANGUAGE; - private static final Set SUPPORTED_LANGUAGES = ImmutableSet.of(EN_US_LANGUAGE, EN_GB_LANGUAGE); private JLanguageTool languageTool; @RuleProperty( @@ -74,7 +79,7 @@ public class SpellingCheck extends SubscriptionVisitorCheck { @RuleProperty( key = "language", - description = "The language to be applied by the spell checker. Supported values are: '" + EN_US_LANGUAGE + "', '" + EN_GB_LANGUAGE + "'.", + description = "The language of the feature files. Supported values are: " + SUPPORTED_LANGUAGES_AS_STRING, defaultValue = DEFAULT_LANGUAGE) private String language = DEFAULT_LANGUAGE; @@ -106,8 +111,8 @@ private void checkSpellingMistakes(String text, SyntaxToken token) { token, m.getFromPos(), m.getToPos(), - "[" + m.getRule().getId() + "] " + m.getMessage() - + ". Suggested correction(s): " + m.getSuggestedReplacements() + ".")); + "[" + m.getRule().getId() + "] " + m.getMessage() + "." + + (!m.getSuggestedReplacements().isEmpty() ? " Suggested correction(s): " + m.getSuggestedReplacements() + "." : ""))); } catch (IOException e) { throw new IllegalStateException("Spell checker failed", e); @@ -115,22 +120,7 @@ private void checkSpellingMistakes(String text, SyntaxToken token) { } private JLanguageTool createLanguageTool() { - English dictionary; - - switch (language) { - case EN_US_LANGUAGE: - dictionary = new AmericanEnglish(); - break; - - case EN_GB_LANGUAGE: - dictionary = new BritishEnglish(); - break; - - default: - throw new IllegalStateException("Unsupported spell check language: " + language); - } - - JLanguageTool jLanguageTool = new JLanguageTool(dictionary); + JLanguageTool jLanguageTool = new JLanguageTool(Languages.getLanguageForShortName(language)); Arrays.stream(rulesToIgnore.split(",")).forEach(jLanguageTool::disableRule); @@ -152,7 +142,7 @@ public void validateParameters() { private String languageParamErrorMessage() { return CheckUtils.paramErrorMessage( this.getClass(), - "language parameter \"" + language + "\" is not valid. Allowed values are '" + EN_US_LANGUAGE + "' and '" + EN_GB_LANGUAGE + "'."); + "language parameter \"" + language + "\" is not valid. Allowed values are: " + SUPPORTED_LANGUAGES_AS_STRING); } @VisibleForTesting diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java index cbd2b20..ad5e61c 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StarStepPrefixCheck.java @@ -21,7 +21,7 @@ import org.sonar.check.Priority; import org.sonar.check.Rule; -import org.sonar.plugins.gherkin.api.tree.StepPrefixTree; +import org.sonar.plugins.gherkin.api.tree.StepTree; import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitorCheck; import org.sonar.squidbridge.annotations.ActivatedByDefault; import org.sonar.squidbridge.annotations.SqaleConstantRemediation; @@ -36,11 +36,11 @@ public class StarStepPrefixCheck extends DoubleDispatchVisitorCheck { @Override - public void visitStepPrefix(StepPrefixTree tree) { - if ("*".equals(tree.text())) { - addPreciseIssue(tree, "Replace this star prefix with one of the Given/When/Then prefixes."); + public void visitStep(StepTree tree) { + if (tree.syntacticType() == StepTree.SyntacticStepType.STAR) { + addPreciseIssue(tree.prefix(), "Replace this star prefix with one of the Given/When/Then prefixes."); } - super.visitStepPrefix(tree); + super.visitStep(tree); } } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepOfUnknownTypeCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepOfUnknownTypeCheck.java index 1cc31a2..1c3becd 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepOfUnknownTypeCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepOfUnknownTypeCheck.java @@ -37,7 +37,7 @@ public class StepOfUnknownTypeCheck extends DoubleDispatchVisitorCheck { @Override public void visitStep(StepTree tree) { - if (tree.type() == StepTree.StepType.UNKNOWN) { + if (tree.semanticType() == StepTree.SemanticStepType.UNKNOWN) { addPreciseIssue(tree.prefix(), "Update the prefix of this unknown type step."); } super.visitStep(tree); diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepsRightOrderCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepsRightOrderCheck.java index 1deeec6..9966909 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepsRightOrderCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/StepsRightOrderCheck.java @@ -53,32 +53,32 @@ public void visitScenarioOutline(ScenarioOutlineTree tree) { private void checkStepOrder(List steps) { - if (steps.size() < 2 || steps.stream().filter(s -> s.type() == StepTree.StepType.UNKNOWN).count() != 0) { + if (steps.size() < 2 || steps.stream().filter(s -> s.semanticType() == StepTree.SemanticStepType.UNKNOWN).count() != 0) { return; } - StepTree.StepType previousStepType = steps.get(0).type(); + StepTree.SemanticStepType previousStepType = steps.get(0).semanticType(); search: for (int i = 1; i < steps.size(); i++) { - switch (steps.get(i).type()) { + switch (steps.get(i).semanticType()) { case GIVEN: - if (previousStepType != StepTree.StepType.GIVEN) { + if (previousStepType != StepTree.SemanticStepType.GIVEN) { addPreciseIssue(steps.get(i).prefix(), "Unexpected Given step. Reorder the steps of this scenario."); break search; } break; case WHEN: - if (previousStepType != StepTree.StepType.GIVEN && previousStepType != StepTree.StepType.WHEN) { + if (previousStepType != StepTree.SemanticStepType.GIVEN && previousStepType != StepTree.SemanticStepType.WHEN) { addPreciseIssue(steps.get(i).prefix(), "Unexpected When step. Reorder the steps of this scenario."); break search; } break; case THEN: - if (previousStepType != StepTree.StepType.WHEN && previousStepType != StepTree.StepType.THEN) { + if (previousStepType != StepTree.SemanticStepType.WHEN && previousStepType != StepTree.SemanticStepType.THEN) { addPreciseIssue(steps.get(i).prefix(), "Unexpected Then step. Reorder the steps of this scenario."); break search; } @@ -88,7 +88,7 @@ private void checkStepOrder(List steps) { break; } - previousStepType = steps.get(i).type(); + previousStepType = steps.get(i).semanticType(); } } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ThenStepRegularExpressionCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ThenStepRegularExpressionCheck.java index 3666957..eee1700 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ThenStepRegularExpressionCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/ThenStepRegularExpressionCheck.java @@ -47,7 +47,7 @@ public class ThenStepRegularExpressionCheck extends DoubleDispatchVisitorCheck { @Override public void visitStep(StepTree tree) { - if (tree.type() == StepTree.StepType.THEN + if (tree.semanticType() == StepTree.SemanticStepType.THEN && !tree.sentence().text().matches(regularExpression)) { addPreciseIssue(tree.sentence(), "Update the sentence to match the following regular expression: " + regularExpression); } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/UseAndButCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/UseAndButCheck.java index 4a3555f..c66d55d 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/UseAndButCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/UseAndButCheck.java @@ -57,21 +57,21 @@ private void checkRedundantStepPrefix(List steps) { return; } - StepTree.StepType previousStepType = steps.get(0).type(); + StepTree.SemanticStepType previousStepType = steps.get(0).semanticType(); for (int i = 1; i < steps.size(); i++) { - if (previousStepType != StepTree.StepType.UNKNOWN - && steps.get(i).type() != StepTree.StepType.UNKNOWN - && previousStepType == steps.get(i).type() - && !"And".equals(steps.get(i).prefix().text()) - && !"But".equals(steps.get(i).prefix().text())) { + if (previousStepType != StepTree.SemanticStepType.UNKNOWN + && steps.get(i).semanticType() != StepTree.SemanticStepType.UNKNOWN + && previousStepType == steps.get(i).semanticType() + && steps.get(i).syntacticType() != StepTree.SyntacticStepType.AND + && steps.get(i).syntacticType() != StepTree.SyntacticStepType.BUT) { addPreciseIssue( steps.get(i).prefix(), - "Replace this redundant " + steps.get(i).prefix().text() + " prefix with And or But."); + "Replace this redundant " + steps.get(i).semanticType().getValue() + " prefix with And or But prefix."); } - - previousStepType = steps.get(i).type(); + + previousStepType = steps.get(i).semanticType(); } } diff --git a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/WhenStepRegularExpressionCheck.java b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/WhenStepRegularExpressionCheck.java index 32a14df..3804c66 100644 --- a/gherkin-checks/src/main/java/org/sonar/gherkin/checks/WhenStepRegularExpressionCheck.java +++ b/gherkin-checks/src/main/java/org/sonar/gherkin/checks/WhenStepRegularExpressionCheck.java @@ -47,7 +47,7 @@ public class WhenStepRegularExpressionCheck extends DoubleDispatchVisitorCheck { @Override public void visitStep(StepTree tree) { - if (tree.type() == StepTree.StepType.WHEN + if (tree.semanticType() == StepTree.SemanticStepType.WHEN && !tree.sentence().text().matches(regularExpression)) { addPreciseIssue(tree.sentence(), "Update the sentence to match the following regular expression: " + regularExpression); } diff --git a/gherkin-checks/src/main/resources/META-INF/org/languagetool/language-module.properties b/gherkin-checks/src/main/resources/META-INF/org/languagetool/language-module.properties new file mode 100644 index 0000000..c099a3c --- /dev/null +++ b/gherkin-checks/src/main/resources/META-INF/org/languagetool/language-module.properties @@ -0,0 +1,44 @@ +languageClasses=org.languagetool.language.English +languageClasses=org.languagetool.language.AmericanEnglish +languageClasses=org.languagetool.language.BritishEnglish +languageClasses=org.languagetool.language.French +languageClasses=org.languagetool.language.AustralianEnglish +languageClasses=org.languagetool.language.CanadianEnglish +languageClasses=org.languagetool.language.NewZealandEnglish +languageClasses=org.languagetool.language.SouthAfricanEnglish +languageClasses=org.languagetool.language.Persian +languageClasses=org.languagetool.language.German +languageClasses=org.languagetool.language.GermanyGerman +languageClasses=org.languagetool.language.AustrianGerman +languageClasses=org.languagetool.language.SwissGerman +languageClasses=org.languagetool.language.SimpleGerman +languageClasses=org.languagetool.language.Polish +languageClasses=org.languagetool.language.Catalan +languageClasses=org.languagetool.language.ValencianCatalan +languageClasses=org.languagetool.language.Italian +languageClasses=org.languagetool.language.Breton +languageClasses=org.languagetool.language.Dutch +languageClasses=org.languagetool.language.Portuguese +languageClasses=org.languagetool.language.PortugalPortuguese +languageClasses=org.languagetool.language.BrazilianPortuguese +languageClasses=org.languagetool.language.Russian +languageClasses=org.languagetool.language.Asturian +languageClasses=org.languagetool.language.Belarusian +languageClasses=org.languagetool.language.Chinese +languageClasses=org.languagetool.language.Danish +languageClasses=org.languagetool.language.Esperanto +languageClasses=org.languagetool.language.Galician +languageClasses=org.languagetool.language.Greek +languageClasses=org.languagetool.language.Icelandic +languageClasses=org.languagetool.language.Japanese +languageClasses=org.languagetool.language.Khmer +languageClasses=org.languagetool.language.Lithuanian +languageClasses=org.languagetool.language.Malayalam +languageClasses=org.languagetool.language.Romanian +languageClasses=org.languagetool.language.Slovak +languageClasses=org.languagetool.language.Slovenian +languageClasses=org.languagetool.language.Spanish +languageClasses=org.languagetool.language.Swedish +languageClasses=org.languagetool.language.Tamil +languageClasses=org.languagetool.language.Tagalog +languageClasses=org.languagetool.language.Ukrainian diff --git a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html index def79ad..85ace98 100644 --- a/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html +++ b/gherkin-checks/src/main/resources/org/sonar/l10n/gherkin/rules/gherkin/indentation.html @@ -1,4 +1,4 @@ -

For readability reasons, code should be properly indented

+

For readability reasons, code should be properly indented.

Noncompliant Code Example

With default value: 2 @@ -11,6 +11,7 @@

Noncompliant Code Example

Compliant Solution

+With default value: 2
 Feature: my feature...
   Scenario: my scenario...
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllSemanticStepTypesInScenarioCheckTest.java
similarity index 96%
rename from gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheckTest.java
rename to gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllSemanticStepTypesInScenarioCheckTest.java
index 5715934..aabdfbd 100644
--- a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllStepTypesInScenarioCheckTest.java
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/AllSemanticStepTypesInScenarioCheckTest.java
@@ -22,7 +22,7 @@
 import org.junit.Test;
 import org.sonar.gherkin.checks.verifier.GherkinCheckVerifier;
 
-public class AllStepTypesInScenarioCheckTest {
+public class AllSemanticStepTypesInScenarioCheckTest {
 
   @Test
   public void test_without_background() {
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedFeatureNamesCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedFeatureNamesCheckTest.java
index 43b48e8..9a3d624 100644
--- a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedFeatureNamesCheckTest.java
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedFeatureNamesCheckTest.java
@@ -90,7 +90,7 @@ public void analyze_several_files() {
 
   private void scanFile(GherkinCheck check, String fileName) {
     GherkinDocumentTree gherkinDocument = (GherkinDocumentTree) GherkinParserBuilder
-      .createParser(Charsets.UTF_8)
+      .createTestParser(Charsets.UTF_8)
       .parse(getTestFile(fileName));
 
     GherkinVisitorContext context = new GherkinVisitorContext(gherkinDocument, getTestFile(fileName));
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedScenarioNamesCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedScenarioNamesCheckTest.java
index 5d259da..5ae2537 100644
--- a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedScenarioNamesCheckTest.java
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/DuplicatedScenarioNamesCheckTest.java
@@ -112,7 +112,7 @@ public void analyze_several_files() {
 
   private void scanFile(GherkinCheck check, String fileName) {
     GherkinDocumentTree gherkinDocument = (GherkinDocumentTree) GherkinParserBuilder
-      .createParser(Charsets.UTF_8)
+      .createTestParser(Charsets.UTF_8)
       .parse(getTestFile(fileName));
 
     GherkinVisitorContext context = new GherkinVisitorContext(gherkinDocument, getTestFile(fileName));
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java
index d390828..5e0044e 100644
--- a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/IndentationCheckTest.java
@@ -48,4 +48,10 @@ public void should_raise_some_issues_with_custom_parameter_value() {
     GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("indentation/indentation-custom-ko.feature"));
   }
 
+  @Test
+  public void should_raise_some_issues_about_single_whitespace() {
+    IndentationCheck check = new IndentationCheck();
+    GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("indentation/indentation-single-whitespace.feature"));
+  }
+
 }
diff --git a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/SpellingCheckTest.java b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/SpellingCheckTest.java
index e0079f1..d2c6529 100644
--- a/gherkin-checks/src/test/java/org/sonar/gherkin/checks/SpellingCheckTest.java
+++ b/gherkin-checks/src/test/java/org/sonar/gherkin/checks/SpellingCheckTest.java
@@ -27,12 +27,20 @@
 public class SpellingCheckTest {
 
   @Test
-  public void should_find_some_spelling_mistakes_and_raise_some_issues() {
+  public void should_find_some_spelling_mistakes_and_raise_some_issues_default_en_US_language() {
     SpellingCheck check = new SpellingCheck();
     check.setWordsToIgnore("blabla,toto");
     GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("spelling/spelling.feature"));
   }
 
+  @Test
+  public void should_find_some_spelling_mistakes_and_raise_some_issues_french() {
+    SpellingCheck check = new SpellingCheck();
+    check.setWordsToIgnore("blabla,toto");
+    check.setLanguage("fr");
+    GherkinCheckVerifier.verify(check, CheckTestUtils.getTestFile("spelling/spelling-fr.feature"));
+  }
+
   @Test
   public void should_not_find_any_spelling_mistake_because_some_spelling_rules_are_exclude() {
     SpellingCheck check = new SpellingCheck();
@@ -45,13 +53,16 @@ public void should_not_find_any_spelling_mistake_because_some_spelling_rules_are
   public void should_throw_an_illegal_state_exception_as_the_language_parameter_is_not_valid() {
     try {
       SpellingCheck check = new SpellingCheck();
-      check.setLanguage("en_NZ");
+      check.setLanguage("abc");
 
       GherkinCheckVerifier.issues(check, CheckTestUtils.getTestFile("spelling/spelling.feature")).noMore();
 
     } catch (IllegalStateException e) {
       assertThat(e.getMessage()).isEqualTo("Check gherkin:spelling (Spelling mistakes should be fixed): "
-        + "language parameter \"en_NZ\" is not valid. Allowed values are 'en_US' and 'en_GB'.");
+        + "language parameter \"abc\" is not valid. Allowed values are: ast-ES, be-BY, br-FR, ca-ES, ca-ES-valencia, "
+        + "da-DK, de, de-AT, de-CH, de-DE, de-DE-x-simple-language, el-GR, en, en-AU, en-CA, en-GB, en-NZ, en-US, "
+        + "en-ZA, eo, es, fa, fr, gl-ES, is-IS, it, ja-JP, km-KH, lt-LT, ml-IN, nl, pl-PL, pt, pt-BR, pt-PT, "
+        + "ro-RO, ru-RU, sk-SK, sl-SI, sv, ta-IN, tl-PH, uk-UA, zh-CN");
     }
   }
 
diff --git a/gherkin-checks/src/test/resources/checks/french.feature b/gherkin-checks/src/test/resources/checks/french.feature
new file mode 100644
index 0000000..283cefd
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/french.feature
@@ -0,0 +1,16 @@
+# language: fr
+Fonctionnalité: My feature french
+  Blabla...
+
+  Scénario: My scenario 1 french
+    # Noncompliant [[sc=5;ec=7]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
+    * Blabla...
+    Lorsque Blabla when...
+    Alors Blabla then...
+
+  Scénario: My scenario 2 french
+    Soit Blabla given...
+    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature
index bb70162..ca7c037 100644
--- a/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-custom-ko.feature
@@ -60,22 +60,19 @@
             | 2    |
 
 
-    # Noncompliant [[sc=16;ec=50]] {{Indent this token at column 15 (currently indented at column 16).}}
-    Scenario:  Scenario 3 - indentation custom KO
+    Scenario: Scenario 3 - indentation custom KO
         Blabla...
         Given Blabla given...
         When Blabla when
         Then Blabla then...
 
-    # Noncompliant [[sc=25;ec=59]] {{Indent this token at column 23 (currently indented at column 25).}}
-    Scenario Outline:   Scenario 4 - indentation custom KO
+    Scenario Outline: Scenario 4 - indentation custom KO
         Blabla...
         Given Blabla given...
         When Blabla when...
         Then Blabla then...
 
-        # Noncompliant [[sc=20;ec=57]] {{Indent this token at column 19 (currently indented at column 20).}}
-        Examples:  Blabla examples indentation custom KO
+        Examples: Blabla examples indentation custom KO
             | data |
             | 1    |
             | 2    |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature
index 85dd1f5..3f9a296 100644
--- a/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-default-ko.feature
@@ -2,7 +2,7 @@
  @tag
 @abc
   # Noncompliant [[sc=3;ec=10]] {{Indent this token at column 1 (currently indented at column 3).}}
-  Feature:  My feature indentation default KO
+  Feature: My feature indentation default KO
     # Noncompliant [[sc=5;ec=14]] {{Indent this token at column 3 (currently indented at column 5).}}
     Blabla...
   Blabla...
@@ -12,7 +12,7 @@
   # Noncompliant [[sc=3;ec=12]] {{Indent this token at column 5 (currently indented at column 3).}}
   Blabla...
     Blabla...
-    # Noncompliant [[sc=6;ec=11]] {{Indent this token at column 5 (currently indented at column 6).}}
+    # Noncompliant [[sc=6;ec=12]] {{Indent this token at column 5 (currently indented at column 6).}}
      Given Blabla given1...
 
     # Noncompliant [[sc=5;ec=6]] {{Indent this token at column 3 (currently indented at column 5).}}
@@ -60,22 +60,19 @@
        | 1    |
       | 2    |
 
-  # Noncompliant [[sc=14;ec=49]] {{Indent this token at column 13 (currently indented at column 14).}}
-  Scenario:  Scenario 3 - indentation default KO
+  Scenario: Scenario 3 - indentation default KO
     Blabla...
     Given Blabla given...
     When Blabla when
     Then Blabla then...
 
-  # Noncompliant [[sc=23;ec=58]] {{Indent this token at column 21 (currently indented at column 23).}}
-  Scenario Outline:   Scenario 4 - indentation default KO
+  Scenario Outline: Scenario 4 - indentation default KO
     Blabla...
     Given Blabla given...
     When Blabla when...
     Then Blabla then...
 
-    # Noncompliant [[sc=16;ec=54]] {{Indent this token at column 15 (currently indented at column 16).}}
-    Examples:  Blabla examples indentation default KO
+    Examples: Blabla examples indentation default KO
       | data |
       | 1    |
       | 2    |
diff --git a/gherkin-checks/src/test/resources/checks/indentation/indentation-single-whitespace.feature b/gherkin-checks/src/test/resources/checks/indentation/indentation-single-whitespace.feature
new file mode 100644
index 0000000..ed7de2b
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/indentation/indentation-single-whitespace.feature
@@ -0,0 +1,40 @@
+# Noncompliant [[sc=11;ec=44;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+Feature:  My feature indentation default WS
+  Blabla...
+  Blabla...
+
+  # Noncompliant [[sc=16;ec=56;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Background:  Blabla background indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given blabla...
+
+  # Noncompliant [[sc=15;ec=48;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Scenario:   Scenario 1 indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given...
+    When Blabla when...
+    Then Blabla then...
+
+  # Noncompliant [[sc=22;ec=55;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Scenario Outline:  Scenario 2 indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given...
+    When Blabla when...
+      | data |
+      | 2    |
+    Then Blabla then...
+      """string
+      Blabla...
+        Blabla...
+      """
+
+    # Noncompliant [[sc=16;ec=54;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+    Examples:  Blabla examples indentation default WS
+      Blabla...
+      Blabla...
+      | data |
+      | 1    |
+      | 2    |
diff --git a/gherkin-checks/src/test/resources/checks/spelling/spelling-fr.feature b/gherkin-checks/src/test/resources/checks/spelling/spelling-fr.feature
new file mode 100644
index 0000000..8eded51
--- /dev/null
+++ b/gherkin-checks/src/test/resources/checks/spelling/spelling-fr.feature
@@ -0,0 +1,7 @@
+# Noncompliant [[sc=27;ec=32]] {{[HUNSPELL_NO_SUGGEST_RULE] Faute de frappe possible trouvée.}}
+Feature: J'achète un joli veelo
+
+  Scenario: Blabla est le nom de mon scénario
+    Given Je suis sur le catalogue
+    When J'ajoute un joli vélo dans mon panier
+    Then Je devrais voir un joli vélo dans mon panier
diff --git a/gherkin-checks/src/test/resources/checks/star-step-prefix.feature b/gherkin-checks/src/test/resources/checks/star-step-prefix.feature
index 698a3a1..345bfc6 100644
--- a/gherkin-checks/src/test/resources/checks/star-step-prefix.feature
+++ b/gherkin-checks/src/test/resources/checks/star-step-prefix.feature
@@ -2,7 +2,7 @@ Feature: My feature Star step prefix
   Blabla...
 
   Scenario: My scenario 1 - Star step prefix
-    # Noncompliant [[sc=5;ec=6]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
+    # Noncompliant [[sc=5;ec=7]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
     * Blabla...
     When Blabla when...
     Then Blabla then...
diff --git a/gherkin-checks/src/test/resources/checks/step-of-unknown-type.feature b/gherkin-checks/src/test/resources/checks/step-of-unknown-type.feature
index 07702d1..47db042 100644
--- a/gherkin-checks/src/test/resources/checks/step-of-unknown-type.feature
+++ b/gherkin-checks/src/test/resources/checks/step-of-unknown-type.feature
@@ -2,7 +2,7 @@ Feature: My feature Step of unknown type
   Blabla...
 
   Scenario: Scenario 1 Step of unknown type
-    # Noncompliant [[sc=5;ec=8]] {{Update the prefix of this unknown type step.}}
+    # Noncompliant [[sc=5;ec=9]] {{Update the prefix of this unknown type step.}}
     And I am a customer
     When I add a product to my cart
     Then I should see the product in my cart
diff --git a/gherkin-checks/src/test/resources/checks/steps-right-order.feature b/gherkin-checks/src/test/resources/checks/steps-right-order.feature
index 05af2a5..1519869 100644
--- a/gherkin-checks/src/test/resources/checks/steps-right-order.feature
+++ b/gherkin-checks/src/test/resources/checks/steps-right-order.feature
@@ -5,13 +5,13 @@ Feature: My feature Steps right order
     Given I am a customer
     When I add a product to my cart
     Then I should see the product in my cart
-    # Noncompliant [[sc=5;ec=9]] {{Unexpected When step. Reorder the steps of this scenario.}}
+    # Noncompliant [[sc=5;ec=10]] {{Unexpected When step. Reorder the steps of this scenario.}}
     When I proceed to the order payment
     Then I should see the order total amount
 
   Scenario Outline: Scenario 2 Steps right order
     Given I am a customer
-    # Noncompliant [[sc=5;ec=9]] {{Unexpected Then step. Reorder the steps of this scenario.}}
+    # Noncompliant [[sc=5;ec=10]] {{Unexpected Then step. Reorder the steps of this scenario.}}
     Then I should see the product in my cart
     And I should see the order total amount 
     Examples:
diff --git a/gherkin-checks/src/test/resources/checks/use-and-but.feature b/gherkin-checks/src/test/resources/checks/use-and-but.feature
index 8cf9263..bb1ad99 100644
--- a/gherkin-checks/src/test/resources/checks/use-and-but.feature
+++ b/gherkin-checks/src/test/resources/checks/use-and-but.feature
@@ -3,14 +3,14 @@ Feature: My feature Use And But
 
   Scenario: Scenario 1 Use And But
     Given Blabla given1...
-    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=11]] {{Replace this redundant Given prefix with And or But prefix.}}
     Given Blabla given2...
     When Blabla when...
     Then Blabla then...
 
   Scenario Outline: Scenario 2 Use And But
     Given Blabla given1...
-    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=11]] {{Replace this redundant Given prefix with And or But prefix.}}
     Given Blabla given2...
     When Blabla when...
     Then Blabla then...
@@ -23,7 +23,7 @@ Feature: My feature Use And But
     Given Blabla given...
     And Blabla...
     When Blabla when...
-    # Noncompliant [[sc=5;ec=9]] {{Replace this redundant When prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant When prefix with And or But prefix.}}
     When Blabla when1...
     Then Blabla then...
 
diff --git a/gherkin-frontend/pom.xml b/gherkin-frontend/pom.xml
index d805fa9..5419b45 100644
--- a/gherkin-frontend/pom.xml
+++ b/gherkin-frontend/pom.xml
@@ -34,6 +34,10 @@
       com.google.guava
       guava
     
+    
+      com.google.code.gson
+      gson
+    
     
       junit
       junit
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialect.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialect.java
new file mode 100644
index 0000000..924ab88
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialect.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube Gherkin Analyzer
+ * Copyright (C) 2016-2016 David RACODON
+ * david.racodon@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.gherkin.parser;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class GherkinDialect {
+
+  private final Map> keywords;
+  private String language;
+
+  public GherkinDialect(String language, Map> keywords) {
+    this.language = language;
+    this.keywords = keywords;
+  }
+
+  public Set getFeatureKeywords() {
+    return keywords.get("feature").stream().collect(Collectors.toSet());
+  }
+
+  public Set getScenarioKeywords() {
+    return keywords.get("scenario").stream().collect(Collectors.toSet());
+  }
+
+  public Set getStepKeywords() {
+    Set result = new HashSet<>();
+    result.addAll(keywords.get("given"));
+    result.addAll(keywords.get("when"));
+    result.addAll(keywords.get("then"));
+    result.addAll(keywords.get("and"));
+    result.addAll(keywords.get("but"));
+    return result;
+  }
+
+  public Set getGivenStepKeywords() {
+    return keywords.get("given").stream().filter(k -> !"* ".equals(k)).collect(Collectors.toSet());
+  }
+
+  public Set getWhenStepKeywords() {
+    return keywords.get("when").stream().filter(k -> !"* ".equals(k)).collect(Collectors.toSet());
+  }
+
+  public Set getThenStepKeywords() {
+    return keywords.get("then").stream().filter(k -> !"* ".equals(k)).collect(Collectors.toSet());
+  }
+
+  public Set getAndStepKeywords() {
+    return keywords.get("and").stream().filter(k -> !"* ".equals(k)).collect(Collectors.toSet());
+  }
+
+  public Set getButStepKeywords() {
+    return keywords.get("but").stream().filter(k -> !"* ".equals(k)).collect(Collectors.toSet());
+  }
+
+  public Set getBackgroundKeywords() {
+    return keywords.get("background").stream().collect(Collectors.toSet());
+  }
+
+  public Set getScenarioOutlineKeywords() {
+    return keywords.get("scenarioOutline").stream().collect(Collectors.toSet());
+  }
+
+  public Set getExamplesKeywords() {
+    return keywords.get("examples").stream().collect(Collectors.toSet());
+  }
+
+  public String getLanguage() {
+    return language;
+  }
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialectProvider.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialectProvider.java
new file mode 100644
index 0000000..96adc4a
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinDialectProvider.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube Gherkin Analyzer
+ * Copyright (C) 2016-2016 David RACODON
+ * david.racodon@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.gherkin.parser;
+
+import com.google.gson.Gson;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public class GherkinDialectProvider {
+
+  public static final Pattern LANGUAGE_DECLARATION_PATTERN = Pattern.compile("#\\s*language:\\s+(.+)\\s*");
+  public static final String DEFAULT_LANGUAGE = "en";
+
+  private static final Map>> DIALECTS;
+
+  static {
+    Gson gson = new Gson();
+    try {
+      Reader dialects = new InputStreamReader(GherkinDialectProvider.class.getResourceAsStream("/org/sonar/gherkin/parser/gherkin-languages.json"), "UTF-8");
+      DIALECTS = gson.fromJson(dialects, Map.class);
+    } catch (UnsupportedEncodingException e) {
+      throw new IllegalStateException("Error while reading gherkin-languages.json file", e);
+    }
+  }
+
+  public static GherkinDialect getDialect(String language) {
+    Map> map = DIALECTS.get(language);
+    if (map == null) {
+      throw new IllegalStateException("Unsupported language: " + language);
+    }
+    return new GherkinDialect(language, map);
+  }
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java
index b949d5f..f9d5c84 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinGrammar.java
@@ -37,6 +37,7 @@ public GherkinDocumentTree GHERKIN_DOCUMENT() {
     return b.nonterminal(GherkinLexicalGrammar.GHERKIN_DOCUMENT).is(
       f.gherkinDocument(
         b.optional(b.token(GherkinLexicalGrammar.BOM)),
+        b.optional(b.token(GherkinLexicalGrammar.LANGUAGE)),
         b.optional(FEATURE()),
         b.token(GherkinLexicalGrammar.EOF)));
   }
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java
index cfa2f6f..2787919 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinLexicalGrammar.java
@@ -23,9 +23,14 @@
 import org.sonar.sslr.grammar.GrammarRuleKey;
 import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
 
+import java.util.Set;
+import java.util.stream.Collectors;
+
 public enum GherkinLexicalGrammar implements GrammarRuleKey {
   GHERKIN_DOCUMENT,
 
+  LANGUAGE,
+
   FEATURE,
   FEATURE_DECLARATION,
   FEATURE_PREFIX,
@@ -85,41 +90,83 @@ public enum GherkinLexicalGrammar implements GrammarRuleKey {
   BOM,
   EOF;
 
-  public static LexerlessGrammarBuilder createGrammar() {
+  public static LexerlessGrammarBuilder createGrammar(String language) {
     LexerlessGrammarBuilder b = LexerlessGrammarBuilder.create();
-    syntax(b);
+    syntax(b, language);
     b.setRootRule(GHERKIN_DOCUMENT);
     return b;
   }
 
-  private static void syntax(LexerlessGrammarBuilder b) {
-
+  private static void syntax(LexerlessGrammarBuilder b, String language) {
     String whitespaceRegex = "(? featureKeywords = dialect.getFeatureKeywords();
+    Set backgroundKeywords = dialect.getBackgroundKeywords();
+    Set scenarioKeywords = dialect.getScenarioKeywords();
+    Set scenarioOutlineKeywords = dialect.getScenarioOutlineKeywords();
+    Set examplesKeywords = dialect.getExamplesKeywords();
+    Set stepKeywords = dialect.getStepKeywords();
+
     b.rule(EOF).is(SPACING, b.token(GenericTokenType.EOF, b.endOfInput()));
     b.rule(BOM).is("\ufeff");
 
     b.rule(TAG_PREFIX).is(SPACING, b.regexp("@"));
     b.rule(TAG_LITERAL).is(b.regexp("[^@\\s]+"));
 
-    b.rule(FEATURE_KEYWORD).is(SPACING, "Feature");
-    b.rule(BACKGROUND_KEYWORD).is(SPACING, "Background");
-    b.rule(SCENARIO_KEYWORD).is(SPACING, "Scenario");
-    b.rule(SCENARIO_OUTLINE_KEYWORD).is(SPACING, "Scenario Outline");
-    b.rule(EXAMPLES_KEYWORD).is(SPACING, "Examples");
-    b.rule(STEP_KEYWORD).is(SPACING, b.firstOf("Given", "When", "Then", "And", "But", "*"));
+    b.rule(FEATURE_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(featureKeywords)));
+    b.rule(BACKGROUND_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(backgroundKeywords)));
+    b.rule(SCENARIO_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(scenarioKeywords)));
+    b.rule(SCENARIO_OUTLINE_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(scenarioOutlineKeywords)));
+    b.rule(EXAMPLES_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(examplesKeywords)));
+    b.rule(STEP_KEYWORD).is(SPACING, b.regexp(convertListToRegexPattern(stepKeywords)));
 
     b.rule(COLON).is(":");
 
     b.rule(NAME_LITERAL).is(WHITESPACE_WITHOUT_LINE_BREAK, b.regexp(trimmedSentence));
-    b.rule(STEP_SENTENCE_LITERAL).is(SPACING, b.regexp("^(?!(Given|When|Then|And|But|\\*|Feature|Background|Scenario|Examples|@|\"\"\"|\\|))" + trimmedSentence));
 
-    b.rule(FEATURE_DESCRIPTION_SENTENCE).is(SPACING, b.regexp("^(?!(Background|Scenario|@))" + trimmedSentence));
-    b.rule(SCENARIO_DESCRIPTION_SENTENCE).is(SPACING, b.regexp("^(?!(Background|Scenario|Given|When|Then|And|But|\\*|Examples|@))" + trimmedSentence));
-    b.rule(EXAMPLES_DESCRIPTION_SENTENCE).is(SPACING, b.regexp("^(?!(Background|Scenario|@|\\|))" + trimmedSentence));
+    b.rule(STEP_SENTENCE_LITERAL).is(b.regexp(trimmedSentence));
+
+    b.rule(FEATURE_DESCRIPTION_SENTENCE).is(
+      SPACING,
+      b.regexp("^(?!("
+        + convertListToRegexPattern(backgroundKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioOutlineKeywords)
+        + "|@))"
+        + trimmedSentence));
+
+    b.rule(SCENARIO_DESCRIPTION_SENTENCE).is(
+      SPACING,
+      b.regexp("^(?!("
+        + convertListToRegexPattern(backgroundKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioOutlineKeywords)
+        + "|"
+        + convertListToRegexPattern(stepKeywords)
+        + "|"
+        + convertListToRegexPattern(examplesKeywords)
+        + "|@))"
+        + trimmedSentence));
+
+    b.rule(EXAMPLES_DESCRIPTION_SENTENCE).is(
+      SPACING,
+      b.regexp("^(?!("
+        + convertListToRegexPattern(backgroundKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioKeywords)
+        + "|"
+        + convertListToRegexPattern(scenarioOutlineKeywords)
+        + "|@|\\|))"
+        + trimmedSentence));
 
     b.rule(DOC_STRING_PREFIX).is(SPACING, "\"\"\"");
     b.rule(DOC_STRING_SUFFIX).is(SPACING_NO_COMMENTS, "\"\"\"");
@@ -128,6 +175,8 @@ private static void syntax(LexerlessGrammarBuilder b) {
 
     b.rule(TABLE_DATA_ROW).is(SPACING, b.regexp("\\|.*\\|"));
 
+    b.rule(LANGUAGE).is(b.regexp(GherkinDialectProvider.LANGUAGE_DECLARATION_PATTERN.pattern()));
+
     b.rule(SPACING).is(
       b.skippedTrivia(b.regexp(whitespaceRegex)),
       b.zeroOrMore(
@@ -141,4 +190,11 @@ private static void syntax(LexerlessGrammarBuilder b) {
       b.skippedTrivia(b.regexp(whitespaceRegex)));
   }
 
+  private static String convertListToRegexPattern(Set keywords) {
+    return keywords
+      .stream()
+      .map(k -> "* ".equals(k) ? "\\* " : k)
+      .collect(Collectors.joining("|"));
+  }
+
 }
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java
index b18c447..c7e1047 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParser.java
@@ -21,6 +21,8 @@
 
 import com.google.common.collect.Lists;
 import com.sonar.sslr.api.typed.ActionParser;
+import org.sonar.plugins.gherkin.api.tree.BasicScenarioTree;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
 import org.sonar.plugins.gherkin.api.tree.Tree;
 import org.sonar.sslr.grammar.GrammarRuleKey;
 import org.sonar.sslr.grammar.LexerlessGrammarBuilder;
@@ -38,17 +40,39 @@ public GherkinParser(Charset charset, LexerlessGrammarBuilder grammarBuilder, Cl
 
   @Override
   public Tree parse(File file) {
-    return createParentLink(super.parse(file));
+    Tree tree = super.parse(file);
+    addParentLink(tree);
+
+    if (tree.is(Tree.Kind.GHERKIN_DOCUMENT)) {
+      addStepType(tree, ((GherkinDocumentTree) tree).language());
+    }
+
+    return tree;
   }
 
-  private static Tree createParentLink(Tree parent) {
+  private static Tree addParentLink(Tree parent) {
     if (!parent.isLeaf()) {
       Lists.newArrayList(parent.childrenIterator())
         .stream()
         .filter(Objects::nonNull)
         .forEach(nextTree -> {
           nextTree.setParent(parent);
-          createParentLink(nextTree);
+          addParentLink(nextTree);
+        });
+    }
+    return parent;
+  }
+
+  private static Tree addStepType(Tree parent, String language) {
+    if (!parent.isLeaf()) {
+      Lists.newArrayList(parent.childrenIterator())
+        .stream()
+        .filter(Objects::nonNull)
+        .forEach(nextTree -> {
+          if (nextTree instanceof BasicScenarioTree) {
+            ((BasicScenarioTree) nextTree).setStepTypes(language);
+          }
+          addStepType(nextTree, language);
         });
     }
     return parent;
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java
index 5a3eff9..f5c407c 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/GherkinParserBuilder.java
@@ -19,7 +19,6 @@
  */
 package org.sonar.gherkin.parser;
 
-import com.google.common.annotations.VisibleForTesting;
 import org.sonar.sslr.grammar.GrammarRuleKey;
 
 import java.nio.charset.Charset;
@@ -29,19 +28,26 @@ public class GherkinParserBuilder {
   private GherkinParserBuilder() {
   }
 
-  public static GherkinParser createParser(Charset charset) {
-    return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT);
+  public static GherkinParser createParser(Charset charset, String language) {
+    return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT, language);
+  }
+
+  public static GherkinParser createTestParser(Charset charset, GrammarRuleKey rootRule, String language) {
+    return createParser(charset, rootRule, language);
   }
 
-  @VisibleForTesting
   public static GherkinParser createTestParser(Charset charset, GrammarRuleKey rootRule) {
-    return createParser(charset, rootRule);
+    return createParser(charset, rootRule, GherkinDialectProvider.DEFAULT_LANGUAGE);
+  }
+
+  public static GherkinParser createTestParser(Charset charset) {
+    return createParser(charset, GherkinLexicalGrammar.GHERKIN_DOCUMENT, GherkinDialectProvider.DEFAULT_LANGUAGE);
   }
 
-  private static GherkinParser createParser(Charset charset, GrammarRuleKey rootRule) {
+  private static GherkinParser createParser(Charset charset, GrammarRuleKey rootRule, String language) {
     return new GherkinParser(
       charset,
-      GherkinLexicalGrammar.createGrammar(),
+      GherkinLexicalGrammar.createGrammar(language),
       GherkinGrammar.class,
       new TreeFactory(),
       new GherkinNodeBuilder(),
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java
index 2a31a86..f1b3800 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/parser/TreeFactory.java
@@ -27,8 +27,8 @@
 
 public class TreeFactory {
 
-  public GherkinDocumentTree gherkinDocument(Optional byteOrderMark, Optional feature, SyntaxToken eof) {
-    return new GherkinDocumentTreeImpl(byteOrderMark.orNull(), feature.orNull(), eof);
+  public GherkinDocumentTree gherkinDocument(Optional byteOrderMark, Optional language, Optional feature, SyntaxToken eof) {
+    return new GherkinDocumentTreeImpl(byteOrderMark.orNull(), language.orNull(), feature.orNull(), eof);
   }
 
   public FeatureTree feature(FeatureDeclarationTree featureDeclaration, Optional background, Optional> allScenarios) {
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractBasicScenarioTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractBasicScenarioTreeImpl.java
index 705f485..fa6b39a 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractBasicScenarioTreeImpl.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/AbstractBasicScenarioTreeImpl.java
@@ -20,6 +20,8 @@
 package org.sonar.gherkin.tree.impl;
 
 import com.google.common.collect.Iterators;
+import org.sonar.gherkin.parser.GherkinDialect;
+import org.sonar.gherkin.parser.GherkinDialectProvider;
 import org.sonar.plugins.gherkin.api.tree.*;
 
 import javax.annotation.Nullable;
@@ -41,8 +43,6 @@ public AbstractBasicScenarioTreeImpl(PrefixTree prefix, SyntaxToken colon, @Null
     this.name = name;
     this.description = description;
     this.steps = steps != null ? steps : new ArrayList<>();
-
-    setStepTypes();
   }
 
   @Override
@@ -79,33 +79,60 @@ public List steps() {
     return steps;
   }
 
-  private void setStepTypes() {
+  @Override
+  public void setStepTypes(String language) {
+    setSyntacticStepTypes(language);
+    setSemanticStepTypes();
+  }
+
+  private void setSemanticStepTypes() {
     for (int i = 0; i < steps.size(); i++) {
       StepTree currentStep = steps.get(i);
-      switch (currentStep.prefix().text()) {
-        case "Given":
-          currentStep.setType(StepTree.StepType.GIVEN);
+      switch (currentStep.syntacticType()) {
+        case GIVEN:
+          currentStep.setSemanticType(StepTree.SemanticStepType.GIVEN);
           break;
 
-        case "When":
-          currentStep.setType(StepTree.StepType.WHEN);
+        case WHEN:
+          currentStep.setSemanticType(StepTree.SemanticStepType.WHEN);
           break;
 
-        case "Then":
-          currentStep.setType(StepTree.StepType.THEN);
+        case THEN:
+          currentStep.setSemanticType(StepTree.SemanticStepType.THEN);
           break;
 
-        case "And":
-        case "But":
+        case AND:
+        case BUT:
           if (i > 0) {
-            currentStep.setType(steps.get(i - 1).type());
+            currentStep.setSemanticType(steps.get(i - 1).semanticType());
           } else {
-            currentStep.setType(StepTree.StepType.UNKNOWN);
+            currentStep.setSemanticType(StepTree.SemanticStepType.UNKNOWN);
           }
           break;
 
         default:
-          currentStep.setType(StepTree.StepType.UNKNOWN);
+          currentStep.setSemanticType(StepTree.SemanticStepType.UNKNOWN);
+      }
+    }
+  }
+
+  private void setSyntacticStepTypes(String language) {
+    GherkinDialect dialect = GherkinDialectProvider.getDialect(language);
+    String prefix;
+    for (StepTree currentStep : steps) {
+      prefix = currentStep.prefix().text();
+      if (dialect.getGivenStepKeywords().contains(prefix)) {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.GIVEN);
+      } else if (dialect.getWhenStepKeywords().contains(prefix)) {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.WHEN);
+      } else if (dialect.getThenStepKeywords().contains(prefix)) {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.THEN);
+      } else if (dialect.getButStepKeywords().contains(prefix)) {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.BUT);
+      } else if (dialect.getAndStepKeywords().contains(prefix)) {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.AND);
+      } else {
+        currentStep.setSyntacticType(StepTree.SyntacticStepType.STAR);
       }
     }
   }
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java
index a08950d..e0f80af 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/GherkinDocumentTreeImpl.java
@@ -20,23 +20,27 @@
 package org.sonar.gherkin.tree.impl;
 
 import com.google.common.collect.Iterators;
-import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+import org.sonar.gherkin.parser.GherkinDialectProvider;
 import org.sonar.plugins.gherkin.api.tree.FeatureTree;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
 import org.sonar.plugins.gherkin.api.tree.SyntaxToken;
 import org.sonar.plugins.gherkin.api.tree.Tree;
 import org.sonar.plugins.gherkin.api.visitors.DoubleDispatchVisitor;
 
 import javax.annotation.Nullable;
 import java.util.Iterator;
+import java.util.regex.Matcher;
 
 public class GherkinDocumentTreeImpl extends GherkinTree implements GherkinDocumentTree {
 
   private final SyntaxToken byteOrderMark;
+  private final SyntaxToken languageDeclaration;
   private final FeatureTree feature;
   private final SyntaxToken eof;
 
-  public GherkinDocumentTreeImpl(@Nullable SyntaxToken byteOrderMark, @Nullable FeatureTree feature, SyntaxToken eof) {
+  public GherkinDocumentTreeImpl(@Nullable SyntaxToken byteOrderMark, @Nullable SyntaxToken languageDeclaration, @Nullable FeatureTree feature, SyntaxToken eof) {
     this.byteOrderMark = byteOrderMark;
+    this.languageDeclaration = languageDeclaration;
     this.feature = feature;
     this.eof = eof;
   }
@@ -48,7 +52,7 @@ public Kind getKind() {
 
   @Override
   public Iterator childrenIterator() {
-    return Iterators.forArray(byteOrderMark, feature, eof);
+    return Iterators.forArray(byteOrderMark, languageDeclaration, feature, eof);
   }
 
   @Override
@@ -56,6 +60,24 @@ public boolean hasByteOrderMark() {
     return byteOrderMark != null;
   }
 
+  @Override
+  public SyntaxToken languageDeclaration() {
+    return languageDeclaration;
+  }
+
+  @Override
+  public String language() {
+    if (languageDeclaration == null) {
+      return GherkinDialectProvider.DEFAULT_LANGUAGE;
+    } else {
+      Matcher matcher = GherkinDialectProvider.LANGUAGE_DECLARATION_PATTERN.matcher(languageDeclaration.text());
+      if (matcher.find()) {
+        return matcher.group(1);
+      }
+      throw new IllegalStateException("Expected language to be explicitly defined in " + languageDeclaration.text());
+    }
+  }
+
   @Override
   @Nullable
   public FeatureTree feature() {
diff --git a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java
index 3089f35..237a821 100644
--- a/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java
+++ b/gherkin-frontend/src/main/java/org/sonar/gherkin/tree/impl/StepTreeImpl.java
@@ -37,7 +37,8 @@ public class StepTreeImpl extends GherkinTree implements StepTree {
   private DocStringTree docString;
   private TableTree table;
   private Set variables;
-  private StepType type;
+  private SemanticStepType semanticType;
+  private SyntacticStepType syntacticType;
 
   public StepTreeImpl(PrefixTree prefix, StepSentenceTree sentence, @Nullable Tree data) {
     this.prefix = prefix;
@@ -68,13 +69,23 @@ public StepSentenceTree sentence() {
   }
 
   @Override
-  public StepType type() {
-    return type;
+  public SyntacticStepType syntacticType() {
+    return syntacticType;
   }
 
   @Override
-  public void setType(StepType type) {
-    this.type = type;
+  public SemanticStepType semanticType() {
+    return semanticType;
+  }
+
+  @Override
+  public void setSyntacticType(SyntacticStepType type) {
+    this.syntacticType = type;
+  }
+
+  @Override
+  public void setSemanticType(SemanticStepType type) {
+    this.semanticType = type;
   }
 
   @Override
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java
index 50335b6..ed25134 100644
--- a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/BasicScenarioTree.java
@@ -21,12 +21,10 @@
 
 import java.util.List;
 
-public interface BasicScenarioTree extends Tree, Nameable, Descriptionable {
-
-  PrefixTree prefix();
-
-  SyntaxToken colon();
+public interface BasicScenarioTree extends Tree, Nameable, Descriptionable, Prefixable {
 
   List steps();
 
+  void setStepTypes(String language);
+
 }
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java
index 1a94ffe..692861a 100644
--- a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/ExamplesTree.java
@@ -21,11 +21,7 @@
 
 import javax.annotation.Nullable;
 
-public interface ExamplesTree extends Tree, Taggable, Nameable, Descriptionable {
-
-  PrefixTree prefix();
-
-  SyntaxToken colon();
+public interface ExamplesTree extends Tree, Taggable, Nameable, Descriptionable, Prefixable {
 
   @Nullable
   TableTree table();
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java
index a44af54..a964383 100644
--- a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/FeatureDeclarationTree.java
@@ -19,10 +19,5 @@
  */
 package org.sonar.plugins.gherkin.api.tree;
 
-public interface FeatureDeclarationTree extends Tree, Taggable, Nameable, Descriptionable {
-
-  PrefixTree prefix();
-
-  SyntaxToken colon();
-
+public interface FeatureDeclarationTree extends Tree, Taggable, Nameable, Descriptionable, Prefixable {
 }
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java
index 3ce2aec..8419eb5 100644
--- a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/GherkinDocumentTree.java
@@ -28,4 +28,8 @@ public interface GherkinDocumentTree extends Tree {
   @Nullable
   FeatureTree feature();
 
+  SyntaxToken languageDeclaration();
+
+  String language();
+
 }
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Prefixable.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Prefixable.java
new file mode 100644
index 0000000..3b48326
--- /dev/null
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/Prefixable.java
@@ -0,0 +1,28 @@
+/*
+ * SonarQube Gherkin Analyzer
+ * Copyright (C) 2016-2016 David RACODON
+ * david.racodon@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.plugins.gherkin.api.tree;
+
+public interface Prefixable {
+
+  PrefixTree prefix();
+
+  SyntaxToken colon();
+
+}
diff --git a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java
index 658d139..2b73617 100644
--- a/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java
+++ b/gherkin-frontend/src/main/java/org/sonar/plugins/gherkin/api/tree/StepTree.java
@@ -24,20 +24,53 @@
 
 public interface StepTree extends Tree {
 
-  enum StepType {
-    GIVEN,
-    WHEN,
-    THEN,
-    UNKNOWN
+  enum SemanticStepType {
+    GIVEN("Given"),
+    WHEN("When"),
+    THEN("Then"),
+    UNKNOWN("Unknown");
+
+    private String value;
+
+    SemanticStepType(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+
+  enum SyntacticStepType {
+    GIVEN("Given"),
+    WHEN("When"),
+    THEN("Then"),
+    STAR("*"),
+    AND("And"),
+    BUT("But");
+
+    private String value;
+
+    SyntacticStepType(String value) {
+      this.value = value;
+    }
+
+    public String getValue() {
+      return value;
+    }
   }
 
   PrefixTree prefix();
 
   StepSentenceTree sentence();
 
-  StepType type();
+  SemanticStepType semanticType();
+
+  void setSemanticType(SemanticStepType type);
+
+  SyntacticStepType syntacticType();
 
-  void setType(StepType type);
+  void setSyntacticType(SyntacticStepType type);
 
   Set variables();
 
diff --git a/gherkin-frontend/src/main/resources/org/sonar/gherkin/parser/gherkin-languages.json b/gherkin-frontend/src/main/resources/org/sonar/gherkin/parser/gherkin-languages.json
new file mode 100644
index 0000000..3fc67f8
--- /dev/null
+++ b/gherkin-frontend/src/main/resources/org/sonar/gherkin/parser/gherkin-languages.json
@@ -0,0 +1,3097 @@
+{
+  "af": {
+    "and": [
+      "* ",
+      "En "
+    ],
+    "background": [
+      "Agtergrond"
+    ],
+    "but": [
+      "* ",
+      "Maar "
+    ],
+    "examples": [
+      "Voorbeelde"
+    ],
+    "feature": [
+      "Funksie",
+      "Besigheid Behoefte",
+      "Vermoë"
+    ],
+    "given": [
+      "* ",
+      "Gegewe "
+    ],
+    "name": "Afrikaans",
+    "native": "Afrikaans",
+    "scenario": [
+      "Situasie"
+    ],
+    "scenarioOutline": [
+      "Situasie Uiteensetting"
+    ],
+    "then": [
+      "* ",
+      "Dan "
+    ],
+    "when": [
+      "* ",
+      "Wanneer "
+    ]
+  },
+  "am": {
+    "and": [
+      "* ",
+      "Եվ "
+    ],
+    "background": [
+      "Կոնտեքստ"
+    ],
+    "but": [
+      "* ",
+      "Բայց "
+    ],
+    "examples": [
+      "Օրինակներ"
+    ],
+    "feature": [
+      "Ֆունկցիոնալություն",
+      "Հատկություն"
+    ],
+    "given": [
+      "* ",
+      "Դիցուք "
+    ],
+    "name": "Armenian",
+    "native": "հայերեն",
+    "scenario": [
+      "Սցենար"
+    ],
+    "scenarioOutline": [
+      "Սցենարի կառուցվացքը"
+    ],
+    "then": [
+      "* ",
+      "Ապա "
+    ],
+    "when": [
+      "* ",
+      "Եթե ",
+      "Երբ "
+    ]
+  },
+  "ar": {
+    "and": [
+      "* ",
+      "و "
+    ],
+    "background": [
+      "الخلفية"
+    ],
+    "but": [
+      "* ",
+      "لكن "
+    ],
+    "examples": [
+      "امثلة"
+    ],
+    "feature": [
+      "خاصية"
+    ],
+    "given": [
+      "* ",
+      "بفرض "
+    ],
+    "name": "Arabic",
+    "native": "العربية",
+    "scenario": [
+      "سيناريو"
+    ],
+    "scenarioOutline": [
+      "سيناريو مخطط"
+    ],
+    "then": [
+      "* ",
+      "اذاً ",
+      "ثم "
+    ],
+    "when": [
+      "* ",
+      "متى ",
+      "عندما "
+    ]
+  },
+  "ast": {
+    "and": [
+      "* ",
+      "Y ",
+      "Ya "
+    ],
+    "background": [
+      "Antecedentes"
+    ],
+    "but": [
+      "* ",
+      "Peru "
+    ],
+    "examples": [
+      "Exemplos"
+    ],
+    "feature": [
+      "Carauterística"
+    ],
+    "given": [
+      "* ",
+      "Dáu ",
+      "Dada ",
+      "Daos ",
+      "Daes "
+    ],
+    "name": "Asturian",
+    "native": "asturianu",
+    "scenario": [
+      "Casu"
+    ],
+    "scenarioOutline": [
+      "Esbozu del casu"
+    ],
+    "then": [
+      "* ",
+      "Entós "
+    ],
+    "when": [
+      "* ",
+      "Cuando "
+    ]
+  },
+  "az": {
+    "and": [
+      "* ",
+      "Və ",
+      "Həm "
+    ],
+    "background": [
+      "Keçmiş",
+      "Kontekst"
+    ],
+    "but": [
+      "* ",
+      "Amma ",
+      "Ancaq "
+    ],
+    "examples": [
+      "Nümunələr"
+    ],
+    "feature": [
+      "Özəllik"
+    ],
+    "given": [
+      "* ",
+      "Tutaq ki ",
+      "Verilir "
+    ],
+    "name": "Azerbaijani",
+    "native": "Azərbaycanca",
+    "scenario": [
+      "Ssenari"
+    ],
+    "scenarioOutline": [
+      "Ssenarinin strukturu"
+    ],
+    "then": [
+      "* ",
+      "O halda "
+    ],
+    "when": [
+      "* ",
+      "Əgər ",
+      "Nə vaxt ki "
+    ]
+  },
+  "bg": {
+    "and": [
+      "* ",
+      "И "
+    ],
+    "background": [
+      "Предистория"
+    ],
+    "but": [
+      "* ",
+      "Но "
+    ],
+    "examples": [
+      "Примери"
+    ],
+    "feature": [
+      "Функционалност"
+    ],
+    "given": [
+      "* ",
+      "Дадено "
+    ],
+    "name": "Bulgarian",
+    "native": "български",
+    "scenario": [
+      "Сценарий"
+    ],
+    "scenarioOutline": [
+      "Рамка на сценарий"
+    ],
+    "then": [
+      "* ",
+      "То "
+    ],
+    "when": [
+      "* ",
+      "Когато "
+    ]
+  },
+  "bm": {
+    "and": [
+      "* ",
+      "Dan "
+    ],
+    "background": [
+      "Latar Belakang"
+    ],
+    "but": [
+      "* ",
+      "Tetapi ",
+      "Tapi "
+    ],
+    "examples": [
+      "Contoh"
+    ],
+    "feature": [
+      "Fungsi"
+    ],
+    "given": [
+      "* ",
+      "Diberi ",
+      "Bagi "
+    ],
+    "name": "Malay",
+    "native": "Bahasa Melayu",
+    "scenario": [
+      "Senario",
+      "Situasi",
+      "Keadaan"
+    ],
+    "scenarioOutline": [
+      "Kerangka Senario",
+      "Kerangka Situasi",
+      "Kerangka Keadaan",
+      "Garis Panduan Senario"
+    ],
+    "then": [
+      "* ",
+      "Maka ",
+      "Kemudian "
+    ],
+    "when": [
+      "* ",
+      "Apabila "
+    ]
+  },
+  "bs": {
+    "and": [
+      "* ",
+      "I ",
+      "A "
+    ],
+    "background": [
+      "Pozadina"
+    ],
+    "but": [
+      "* ",
+      "Ali "
+    ],
+    "examples": [
+      "Primjeri"
+    ],
+    "feature": [
+      "Karakteristika"
+    ],
+    "given": [
+      "* ",
+      "Dato "
+    ],
+    "name": "Bosnian",
+    "native": "Bosanski",
+    "scenario": [
+      "Scenariju",
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Scenariju-obris",
+      "Scenario-outline"
+    ],
+    "then": [
+      "* ",
+      "Zatim "
+    ],
+    "when": [
+      "* ",
+      "Kada "
+    ]
+  },
+  "ca": {
+    "and": [
+      "* ",
+      "I "
+    ],
+    "background": [
+      "Rerefons",
+      "Antecedents"
+    ],
+    "but": [
+      "* ",
+      "Però "
+    ],
+    "examples": [
+      "Exemples"
+    ],
+    "feature": [
+      "Característica",
+      "Funcionalitat"
+    ],
+    "given": [
+      "* ",
+      "Donat ",
+      "Donada ",
+      "Atès ",
+      "Atesa "
+    ],
+    "name": "Catalan",
+    "native": "català",
+    "scenario": [
+      "Escenari"
+    ],
+    "scenarioOutline": [
+      "Esquema de l'escenari"
+    ],
+    "then": [
+      "* ",
+      "Aleshores ",
+      "Cal "
+    ],
+    "when": [
+      "* ",
+      "Quan "
+    ]
+  },
+  "cs": {
+    "and": [
+      "* ",
+      "A také ",
+      "A "
+    ],
+    "background": [
+      "Pozadí",
+      "Kontext"
+    ],
+    "but": [
+      "* ",
+      "Ale "
+    ],
+    "examples": [
+      "Příklady"
+    ],
+    "feature": [
+      "Požadavek"
+    ],
+    "given": [
+      "* ",
+      "Pokud ",
+      "Za předpokladu "
+    ],
+    "name": "Czech",
+    "native": "Česky",
+    "scenario": [
+      "Scénář"
+    ],
+    "scenarioOutline": [
+      "Náčrt Scénáře",
+      "Osnova scénáře"
+    ],
+    "then": [
+      "* ",
+      "Pak "
+    ],
+    "when": [
+      "* ",
+      "Když "
+    ]
+  },
+  "cy-GB": {
+    "and": [
+      "* ",
+      "A "
+    ],
+    "background": [
+      "Cefndir"
+    ],
+    "but": [
+      "* ",
+      "Ond "
+    ],
+    "examples": [
+      "Enghreifftiau"
+    ],
+    "feature": [
+      "Arwedd"
+    ],
+    "given": [
+      "* ",
+      "Anrhegedig a "
+    ],
+    "name": "Welsh",
+    "native": "Cymraeg",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Scenario Amlinellol"
+    ],
+    "then": [
+      "* ",
+      "Yna "
+    ],
+    "when": [
+      "* ",
+      "Pryd "
+    ]
+  },
+  "da": {
+    "and": [
+      "* ",
+      "Og "
+    ],
+    "background": [
+      "Baggrund"
+    ],
+    "but": [
+      "* ",
+      "Men "
+    ],
+    "examples": [
+      "Eksempler"
+    ],
+    "feature": [
+      "Egenskab"
+    ],
+    "given": [
+      "* ",
+      "Givet "
+    ],
+    "name": "Danish",
+    "native": "dansk",
+    "scenario": [
+      "Scenarie"
+    ],
+    "scenarioOutline": [
+      "Abstrakt Scenario"
+    ],
+    "then": [
+      "* ",
+      "Så "
+    ],
+    "when": [
+      "* ",
+      "Når "
+    ]
+  },
+  "de": {
+    "and": [
+      "* ",
+      "Und "
+    ],
+    "background": [
+      "Grundlage"
+    ],
+    "but": [
+      "* ",
+      "Aber "
+    ],
+    "examples": [
+      "Beispiele"
+    ],
+    "feature": [
+      "Funktionalität"
+    ],
+    "given": [
+      "* ",
+      "Angenommen ",
+      "Gegeben sei ",
+      "Gegeben seien "
+    ],
+    "name": "German",
+    "native": "Deutsch",
+    "scenario": [
+      "Szenario"
+    ],
+    "scenarioOutline": [
+      "Szenariogrundriss"
+    ],
+    "then": [
+      "* ",
+      "Dann "
+    ],
+    "when": [
+      "* ",
+      "Wenn "
+    ]
+  },
+  "el": {
+    "and": [
+      "* ",
+      "Και "
+    ],
+    "background": [
+      "Υπόβαθρο"
+    ],
+    "but": [
+      "* ",
+      "Αλλά "
+    ],
+    "examples": [
+      "Παραδείγματα",
+      "Σενάρια"
+    ],
+    "feature": [
+      "Δυνατότητα",
+      "Λειτουργία"
+    ],
+    "given": [
+      "* ",
+      "Δεδομένου "
+    ],
+    "name": "Greek",
+    "native": "Ελληνικά",
+    "scenario": [
+      "Σενάριο"
+    ],
+    "scenarioOutline": [
+      "Περιγραφή Σεναρίου"
+    ],
+    "then": [
+      "* ",
+      "Τότε "
+    ],
+    "when": [
+      "* ",
+      "Όταν "
+    ]
+  },
+  "em": {
+    "and": [
+      "* ",
+      "😂"
+    ],
+    "background": [
+      "💤"
+    ],
+    "but": [
+      "* ",
+      "😔"
+    ],
+    "examples": [
+      "📓"
+    ],
+    "feature": [
+      "📚"
+    ],
+    "given": [
+      "* ",
+      "😐"
+    ],
+    "name": "Emoji",
+    "native": "😀",
+    "scenario": [
+      "📕"
+    ],
+    "scenarioOutline": [
+      "📖"
+    ],
+    "then": [
+      "* ",
+      "🙏"
+    ],
+    "when": [
+      "* ",
+      "🎬"
+    ]
+  },
+  "en": {
+    "and": [
+      "* ",
+      "And "
+    ],
+    "background": [
+      "Background"
+    ],
+    "but": [
+      "* ",
+      "But "
+    ],
+    "examples": [
+      "Examples",
+      "Scenarios"
+    ],
+    "feature": [
+      "Feature",
+      "Business Need",
+      "Ability"
+    ],
+    "given": [
+      "* ",
+      "Given "
+    ],
+    "name": "English",
+    "native": "English",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Scenario Outline",
+      "Scenario Template"
+    ],
+    "then": [
+      "* ",
+      "Then "
+    ],
+    "when": [
+      "* ",
+      "When "
+    ]
+  },
+  "en-Scouse": {
+    "and": [
+      "* ",
+      "An "
+    ],
+    "background": [
+      "Dis is what went down"
+    ],
+    "but": [
+      "* ",
+      "Buh "
+    ],
+    "examples": [
+      "Examples"
+    ],
+    "feature": [
+      "Feature"
+    ],
+    "given": [
+      "* ",
+      "Givun ",
+      "Youse know when youse got "
+    ],
+    "name": "Scouse",
+    "native": "Scouse",
+    "scenario": [
+      "The thing of it is"
+    ],
+    "scenarioOutline": [
+      "Wharrimean is"
+    ],
+    "then": [
+      "* ",
+      "Dun ",
+      "Den youse gotta "
+    ],
+    "when": [
+      "* ",
+      "Wun ",
+      "Youse know like when "
+    ]
+  },
+  "en-au": {
+    "and": [
+      "* ",
+      "Too right "
+    ],
+    "background": [
+      "First off"
+    ],
+    "but": [
+      "* ",
+      "Yeah nah "
+    ],
+    "examples": [
+      "You'll wanna"
+    ],
+    "feature": [
+      "Pretty much"
+    ],
+    "given": [
+      "* ",
+      "Y'know "
+    ],
+    "name": "Australian",
+    "native": "Australian",
+    "scenario": [
+      "Awww, look mate"
+    ],
+    "scenarioOutline": [
+      "Reckon it's like"
+    ],
+    "then": [
+      "* ",
+      "But at the end of the day I reckon "
+    ],
+    "when": [
+      "* ",
+      "It's just unbelievable "
+    ]
+  },
+  "en-lol": {
+    "and": [
+      "* ",
+      "AN "
+    ],
+    "background": [
+      "B4"
+    ],
+    "but": [
+      "* ",
+      "BUT "
+    ],
+    "examples": [
+      "EXAMPLZ"
+    ],
+    "feature": [
+      "OH HAI"
+    ],
+    "given": [
+      "* ",
+      "I CAN HAZ "
+    ],
+    "name": "LOLCAT",
+    "native": "LOLCAT",
+    "scenario": [
+      "MISHUN"
+    ],
+    "scenarioOutline": [
+      "MISHUN SRSLY"
+    ],
+    "then": [
+      "* ",
+      "DEN "
+    ],
+    "when": [
+      "* ",
+      "WEN "
+    ]
+  },
+  "en-old": {
+    "and": [
+      "* ",
+      "Ond ",
+      "7 "
+    ],
+    "background": [
+      "Aer",
+      "Ær"
+    ],
+    "but": [
+      "* ",
+      "Ac "
+    ],
+    "examples": [
+      "Se the",
+      "Se þe",
+      "Se ðe"
+    ],
+    "feature": [
+      "Hwaet",
+      "Hwæt"
+    ],
+    "given": [
+      "* ",
+      "Thurh ",
+      "Þurh ",
+      "Ðurh "
+    ],
+    "name": "Old English",
+    "native": "Englisc",
+    "scenario": [
+      "Swa"
+    ],
+    "scenarioOutline": [
+      "Swa hwaer swa",
+      "Swa hwær swa"
+    ],
+    "then": [
+      "* ",
+      "Tha ",
+      "Þa ",
+      "Ða ",
+      "Tha the ",
+      "Þa þe ",
+      "Ða ðe "
+    ],
+    "when": [
+      "* ",
+      "Tha ",
+      "Þa ",
+      "Ða "
+    ]
+  },
+  "en-pirate": {
+    "and": [
+      "* ",
+      "Aye "
+    ],
+    "background": [
+      "Yo-ho-ho"
+    ],
+    "but": [
+      "* ",
+      "Avast! "
+    ],
+    "examples": [
+      "Dead men tell no tales"
+    ],
+    "feature": [
+      "Ahoy matey!"
+    ],
+    "given": [
+      "* ",
+      "Gangway! "
+    ],
+    "name": "Pirate",
+    "native": "Pirate",
+    "scenario": [
+      "Heave to"
+    ],
+    "scenarioOutline": [
+      "Shiver me timbers"
+    ],
+    "then": [
+      "* ",
+      "Let go and haul "
+    ],
+    "when": [
+      "* ",
+      "Blimey! "
+    ]
+  },
+  "eo": {
+    "and": [
+      "* ",
+      "Kaj "
+    ],
+    "background": [
+      "Fono"
+    ],
+    "but": [
+      "* ",
+      "Sed "
+    ],
+    "examples": [
+      "Ekzemploj"
+    ],
+    "feature": [
+      "Trajto"
+    ],
+    "given": [
+      "* ",
+      "Donitaĵo ",
+      "Komence "
+    ],
+    "name": "Esperanto",
+    "native": "Esperanto",
+    "scenario": [
+      "Scenaro",
+      "Kazo"
+    ],
+    "scenarioOutline": [
+      "Konturo de la scenaro",
+      "Skizo",
+      "Kazo-skizo"
+    ],
+    "then": [
+      "* ",
+      "Do "
+    ],
+    "when": [
+      "* ",
+      "Se "
+    ]
+  },
+  "es": {
+    "and": [
+      "* ",
+      "Y ",
+      "E "
+    ],
+    "background": [
+      "Antecedentes"
+    ],
+    "but": [
+      "* ",
+      "Pero "
+    ],
+    "examples": [
+      "Ejemplos"
+    ],
+    "feature": [
+      "Característica"
+    ],
+    "given": [
+      "* ",
+      "Dado ",
+      "Dada ",
+      "Dados ",
+      "Dadas "
+    ],
+    "name": "Spanish",
+    "native": "español",
+    "scenario": [
+      "Escenario"
+    ],
+    "scenarioOutline": [
+      "Esquema del escenario"
+    ],
+    "then": [
+      "* ",
+      "Entonces "
+    ],
+    "when": [
+      "* ",
+      "Cuando "
+    ]
+  },
+  "et": {
+    "and": [
+      "* ",
+      "Ja "
+    ],
+    "background": [
+      "Taust"
+    ],
+    "but": [
+      "* ",
+      "Kuid "
+    ],
+    "examples": [
+      "Juhtumid"
+    ],
+    "feature": [
+      "Omadus"
+    ],
+    "given": [
+      "* ",
+      "Eeldades "
+    ],
+    "name": "Estonian",
+    "native": "eesti keel",
+    "scenario": [
+      "Stsenaarium"
+    ],
+    "scenarioOutline": [
+      "Raamstsenaarium"
+    ],
+    "then": [
+      "* ",
+      "Siis "
+    ],
+    "when": [
+      "* ",
+      "Kui "
+    ]
+  },
+  "fa": {
+    "and": [
+      "* ",
+      "و "
+    ],
+    "background": [
+      "زمینه"
+    ],
+    "but": [
+      "* ",
+      "اما "
+    ],
+    "examples": [
+      "نمونه ها"
+    ],
+    "feature": [
+      "وِیژگی"
+    ],
+    "given": [
+      "* ",
+      "با فرض "
+    ],
+    "name": "Persian",
+    "native": "فارسی",
+    "scenario": [
+      "سناریو"
+    ],
+    "scenarioOutline": [
+      "الگوی سناریو"
+    ],
+    "then": [
+      "* ",
+      "آنگاه "
+    ],
+    "when": [
+      "* ",
+      "هنگامی "
+    ]
+  },
+  "fi": {
+    "and": [
+      "* ",
+      "Ja "
+    ],
+    "background": [
+      "Tausta"
+    ],
+    "but": [
+      "* ",
+      "Mutta "
+    ],
+    "examples": [
+      "Tapaukset"
+    ],
+    "feature": [
+      "Ominaisuus"
+    ],
+    "given": [
+      "* ",
+      "Oletetaan "
+    ],
+    "name": "Finnish",
+    "native": "suomi",
+    "scenario": [
+      "Tapaus"
+    ],
+    "scenarioOutline": [
+      "Tapausaihio"
+    ],
+    "then": [
+      "* ",
+      "Niin "
+    ],
+    "when": [
+      "* ",
+      "Kun "
+    ]
+  },
+  "fr": {
+    "and": [
+      "* ",
+      "Et que ",
+      "Et qu'",
+      "Et "
+    ],
+    "background": [
+      "Contexte"
+    ],
+    "but": [
+      "* ",
+      "Mais que ",
+      "Mais qu'",
+      "Mais "
+    ],
+    "examples": [
+      "Exemples"
+    ],
+    "feature": [
+      "Fonctionnalité"
+    ],
+    "given": [
+      "* ",
+      "Soit ",
+      "Etant donné que ",
+      "Etant donné qu'",
+      "Etant donné ",
+      "Etant donnée ",
+      "Etant donnés ",
+      "Etant données ",
+      "Étant donné que ",
+      "Étant donné qu'",
+      "Étant donné ",
+      "Étant donnée ",
+      "Étant donnés ",
+      "Étant données "
+    ],
+    "name": "French",
+    "native": "français",
+    "scenario": [
+      "Scénario"
+    ],
+    "scenarioOutline": [
+      "Plan du scénario",
+      "Plan du Scénario"
+    ],
+    "then": [
+      "* ",
+      "Alors "
+    ],
+    "when": [
+      "* ",
+      "Quand ",
+      "Lorsque ",
+      "Lorsqu'"
+    ]
+  },
+  "ga": {
+    "and": [
+      "* ",
+      "Agus"
+    ],
+    "background": [
+      "Cúlra"
+    ],
+    "but": [
+      "* ",
+      "Ach"
+    ],
+    "examples": [
+      "Samplaí"
+    ],
+    "feature": [
+      "Gné"
+    ],
+    "given": [
+      "* ",
+      "Cuir i gcás go",
+      "Cuir i gcás nach",
+      "Cuir i gcás gur",
+      "Cuir i gcás nár"
+    ],
+    "name": "Irish",
+    "native": "Gaeilge",
+    "scenario": [
+      "Cás"
+    ],
+    "scenarioOutline": [
+      "Cás Achomair"
+    ],
+    "then": [
+      "* ",
+      "Ansin"
+    ],
+    "when": [
+      "* ",
+      "Nuair a",
+      "Nuair nach",
+      "Nuair ba",
+      "Nuair nár"
+    ]
+  },
+  "gj": {
+    "and": [
+      "* ",
+      "અને "
+    ],
+    "background": [
+      "બેકગ્રાઉન્ડ"
+    ],
+    "but": [
+      "* ",
+      "પણ "
+    ],
+    "examples": [
+      "ઉદાહરણો"
+    ],
+    "feature": [
+      "લક્ષણ",
+      "વ્યાપાર જરૂર",
+      "ક્ષમતા"
+    ],
+    "given": [
+      "* ",
+      "આપેલ છે "
+    ],
+    "name": "Gujarati",
+    "native": "ગુજરાતી",
+    "scenario": [
+      "સ્થિતિ"
+    ],
+    "scenarioOutline": [
+      "પરિદ્દશ્ય રૂપરેખા",
+      "પરિદ્દશ્ય ઢાંચો"
+    ],
+    "then": [
+      "* ",
+      "પછી "
+    ],
+    "when": [
+      "* ",
+      "ક્યારે "
+    ]
+  },
+  "gl": {
+    "and": [
+      "* ",
+      "E "
+    ],
+    "background": [
+      "Contexto"
+    ],
+    "but": [
+      "* ",
+      "Mais ",
+      "Pero "
+    ],
+    "examples": [
+      "Exemplos"
+    ],
+    "feature": [
+      "Característica"
+    ],
+    "given": [
+      "* ",
+      "Dado ",
+      "Dada ",
+      "Dados ",
+      "Dadas "
+    ],
+    "name": "Galician",
+    "native": "galego",
+    "scenario": [
+      "Escenario"
+    ],
+    "scenarioOutline": [
+      "Esbozo do escenario"
+    ],
+    "then": [
+      "* ",
+      "Entón ",
+      "Logo "
+    ],
+    "when": [
+      "* ",
+      "Cando "
+    ]
+  },
+  "he": {
+    "and": [
+      "* ",
+      "וגם "
+    ],
+    "background": [
+      "רקע"
+    ],
+    "but": [
+      "* ",
+      "אבל "
+    ],
+    "examples": [
+      "דוגמאות"
+    ],
+    "feature": [
+      "תכונה"
+    ],
+    "given": [
+      "* ",
+      "בהינתן "
+    ],
+    "name": "Hebrew",
+    "native": "עברית",
+    "scenario": [
+      "תרחיש"
+    ],
+    "scenarioOutline": [
+      "תבנית תרחיש"
+    ],
+    "then": [
+      "* ",
+      "אז ",
+      "אזי "
+    ],
+    "when": [
+      "* ",
+      "כאשר "
+    ]
+  },
+  "hi": {
+    "and": [
+      "* ",
+      "और ",
+      "तथा "
+    ],
+    "background": [
+      "पृष्ठभूमि"
+    ],
+    "but": [
+      "* ",
+      "पर ",
+      "परन्तु ",
+      "किन्तु "
+    ],
+    "examples": [
+      "उदाहरण"
+    ],
+    "feature": [
+      "रूप लेख"
+    ],
+    "given": [
+      "* ",
+      "अगर ",
+      "यदि ",
+      "चूंकि "
+    ],
+    "name": "Hindi",
+    "native": "हिंदी",
+    "scenario": [
+      "परिदृश्य"
+    ],
+    "scenarioOutline": [
+      "परिदृश्य रूपरेखा"
+    ],
+    "then": [
+      "* ",
+      "तब ",
+      "तदा "
+    ],
+    "when": [
+      "* ",
+      "जब ",
+      "कदा "
+    ]
+  },
+  "hr": {
+    "and": [
+      "* ",
+      "I "
+    ],
+    "background": [
+      "Pozadina"
+    ],
+    "but": [
+      "* ",
+      "Ali "
+    ],
+    "examples": [
+      "Primjeri",
+      "Scenariji"
+    ],
+    "feature": [
+      "Osobina",
+      "Mogućnost",
+      "Mogucnost"
+    ],
+    "given": [
+      "* ",
+      "Zadan ",
+      "Zadani ",
+      "Zadano "
+    ],
+    "name": "Croatian",
+    "native": "hrvatski",
+    "scenario": [
+      "Scenarij"
+    ],
+    "scenarioOutline": [
+      "Skica",
+      "Koncept"
+    ],
+    "then": [
+      "* ",
+      "Onda "
+    ],
+    "when": [
+      "* ",
+      "Kada ",
+      "Kad "
+    ]
+  },
+  "ht": {
+    "and": [
+      "* ",
+      "Ak ",
+      "Epi ",
+      "E "
+    ],
+    "background": [
+      "Kontèks",
+      "Istorik"
+    ],
+    "but": [
+      "* ",
+      "Men "
+    ],
+    "examples": [
+      "Egzanp"
+    ],
+    "feature": [
+      "Karakteristik",
+      "Mak",
+      "Fonksyonalite"
+    ],
+    "given": [
+      "* ",
+      "Sipoze ",
+      "Sipoze ke ",
+      "Sipoze Ke "
+    ],
+    "name": "Creole",
+    "native": "kreyòl",
+    "scenario": [
+      "Senaryo"
+    ],
+    "scenarioOutline": [
+      "Plan senaryo",
+      "Plan Senaryo",
+      "Senaryo deskripsyon",
+      "Senaryo Deskripsyon",
+      "Dyagram senaryo",
+      "Dyagram Senaryo"
+    ],
+    "then": [
+      "* ",
+      "Lè sa a ",
+      "Le sa a "
+    ],
+    "when": [
+      "* ",
+      "Lè ",
+      "Le "
+    ]
+  },
+  "hu": {
+    "and": [
+      "* ",
+      "És "
+    ],
+    "background": [
+      "Háttér"
+    ],
+    "but": [
+      "* ",
+      "De "
+    ],
+    "examples": [
+      "Példák"
+    ],
+    "feature": [
+      "Jellemző"
+    ],
+    "given": [
+      "* ",
+      "Amennyiben ",
+      "Adott "
+    ],
+    "name": "Hungarian",
+    "native": "magyar",
+    "scenario": [
+      "Forgatókönyv"
+    ],
+    "scenarioOutline": [
+      "Forgatókönyv vázlat"
+    ],
+    "then": [
+      "* ",
+      "Akkor "
+    ],
+    "when": [
+      "* ",
+      "Majd ",
+      "Ha ",
+      "Amikor "
+    ]
+  },
+  "id": {
+    "and": [
+      "* ",
+      "Dan "
+    ],
+    "background": [
+      "Dasar"
+    ],
+    "but": [
+      "* ",
+      "Tapi "
+    ],
+    "examples": [
+      "Contoh"
+    ],
+    "feature": [
+      "Fitur"
+    ],
+    "given": [
+      "* ",
+      "Dengan "
+    ],
+    "name": "Indonesian",
+    "native": "Bahasa Indonesia",
+    "scenario": [
+      "Skenario"
+    ],
+    "scenarioOutline": [
+      "Skenario konsep"
+    ],
+    "then": [
+      "* ",
+      "Maka "
+    ],
+    "when": [
+      "* ",
+      "Ketika "
+    ]
+  },
+  "is": {
+    "and": [
+      "* ",
+      "Og "
+    ],
+    "background": [
+      "Bakgrunnur"
+    ],
+    "but": [
+      "* ",
+      "En "
+    ],
+    "examples": [
+      "Dæmi",
+      "Atburðarásir"
+    ],
+    "feature": [
+      "Eiginleiki"
+    ],
+    "given": [
+      "* ",
+      "Ef "
+    ],
+    "name": "Icelandic",
+    "native": "Íslenska",
+    "scenario": [
+      "Atburðarás"
+    ],
+    "scenarioOutline": [
+      "Lýsing Atburðarásar",
+      "Lýsing Dæma"
+    ],
+    "then": [
+      "* ",
+      "Þá "
+    ],
+    "when": [
+      "* ",
+      "Þegar "
+    ]
+  },
+  "it": {
+    "and": [
+      "* ",
+      "E "
+    ],
+    "background": [
+      "Contesto"
+    ],
+    "but": [
+      "* ",
+      "Ma "
+    ],
+    "examples": [
+      "Esempi"
+    ],
+    "feature": [
+      "Funzionalità"
+    ],
+    "given": [
+      "* ",
+      "Dato ",
+      "Data ",
+      "Dati ",
+      "Date "
+    ],
+    "name": "Italian",
+    "native": "italiano",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Schema dello scenario"
+    ],
+    "then": [
+      "* ",
+      "Allora "
+    ],
+    "when": [
+      "* ",
+      "Quando "
+    ]
+  },
+  "ja": {
+    "and": [
+      "* ",
+      "かつ"
+    ],
+    "background": [
+      "背景"
+    ],
+    "but": [
+      "* ",
+      "しかし",
+      "但し",
+      "ただし"
+    ],
+    "examples": [
+      "例",
+      "サンプル"
+    ],
+    "feature": [
+      "フィーチャ",
+      "機能"
+    ],
+    "given": [
+      "* ",
+      "前提"
+    ],
+    "name": "Japanese",
+    "native": "日本語",
+    "scenario": [
+      "シナリオ"
+    ],
+    "scenarioOutline": [
+      "シナリオアウトライン",
+      "シナリオテンプレート",
+      "テンプレ",
+      "シナリオテンプレ"
+    ],
+    "then": [
+      "* ",
+      "ならば"
+    ],
+    "when": [
+      "* ",
+      "もし"
+    ]
+  },
+  "jv": {
+    "and": [
+      "* ",
+      "Lan "
+    ],
+    "background": [
+      "Dasar"
+    ],
+    "but": [
+      "* ",
+      "Tapi ",
+      "Nanging ",
+      "Ananging "
+    ],
+    "examples": [
+      "Conto",
+      "Contone"
+    ],
+    "feature": [
+      "Fitur"
+    ],
+    "given": [
+      "* ",
+      "Nalika ",
+      "Nalikaning "
+    ],
+    "name": "Javanese",
+    "native": "Basa Jawa",
+    "scenario": [
+      "Skenario"
+    ],
+    "scenarioOutline": [
+      "Konsep skenario"
+    ],
+    "then": [
+      "* ",
+      "Njuk ",
+      "Banjur "
+    ],
+    "when": [
+      "* ",
+      "Manawa ",
+      "Menawa "
+    ]
+  },
+  "ka": {
+    "and": [
+      "* ",
+      "და"
+    ],
+    "background": [
+      "კონტექსტი"
+    ],
+    "but": [
+      "* ",
+      "მაგ­რამ"
+    ],
+    "examples": [
+      "მაგალითები"
+    ],
+    "feature": [
+      "თვისება"
+    ],
+    "given": [
+      "* ",
+      "მოცემული"
+    ],
+    "name": "Georgian",
+    "native": "ქართველი",
+    "scenario": [
+      "სცენარის"
+    ],
+    "scenarioOutline": [
+      "სცენარის ნიმუში"
+    ],
+    "then": [
+      "* ",
+      "მაშინ"
+    ],
+    "when": [
+      "* ",
+      "როდესაც"
+    ]
+  },
+  "kn": {
+    "and": [
+      "* ",
+      "ಮತ್ತು "
+    ],
+    "background": [
+      "ಹಿನ್ನೆಲೆ"
+    ],
+    "but": [
+      "* ",
+      "ಆದರೆ "
+    ],
+    "examples": [
+      "ಉದಾಹರಣೆಗಳು"
+    ],
+    "feature": [
+      "ಹೆಚ್ಚಳ"
+    ],
+    "given": [
+      "* ",
+      "ನೀಡಿದ "
+    ],
+    "name": "Kannada",
+    "native": "ಕನ್ನಡ",
+    "scenario": [
+      "ಕಥಾಸಾರಾಂಶ"
+    ],
+    "scenarioOutline": [
+      "ವಿವರಣೆ"
+    ],
+    "then": [
+      "* ",
+      "ನಂತರ "
+    ],
+    "when": [
+      "* ",
+      "ಸ್ಥಿತಿಯನ್ನು "
+    ]
+  },
+  "ko": {
+    "and": [
+      "* ",
+      "그리고"
+    ],
+    "background": [
+      "배경"
+    ],
+    "but": [
+      "* ",
+      "하지만",
+      "단"
+    ],
+    "examples": [
+      "예"
+    ],
+    "feature": [
+      "기능"
+    ],
+    "given": [
+      "* ",
+      "조건",
+      "먼저"
+    ],
+    "name": "Korean",
+    "native": "한국어",
+    "scenario": [
+      "시나리오"
+    ],
+    "scenarioOutline": [
+      "시나리오 개요"
+    ],
+    "then": [
+      "* ",
+      "그러면"
+    ],
+    "when": [
+      "* ",
+      "만일",
+      "만약"
+    ]
+  },
+  "lt": {
+    "and": [
+      "* ",
+      "Ir "
+    ],
+    "background": [
+      "Kontekstas"
+    ],
+    "but": [
+      "* ",
+      "Bet "
+    ],
+    "examples": [
+      "Pavyzdžiai",
+      "Scenarijai",
+      "Variantai"
+    ],
+    "feature": [
+      "Savybė"
+    ],
+    "given": [
+      "* ",
+      "Duota "
+    ],
+    "name": "Lithuanian",
+    "native": "lietuvių kalba",
+    "scenario": [
+      "Scenarijus"
+    ],
+    "scenarioOutline": [
+      "Scenarijaus šablonas"
+    ],
+    "then": [
+      "* ",
+      "Tada "
+    ],
+    "when": [
+      "* ",
+      "Kai "
+    ]
+  },
+  "lu": {
+    "and": [
+      "* ",
+      "an ",
+      "a "
+    ],
+    "background": [
+      "Hannergrond"
+    ],
+    "but": [
+      "* ",
+      "awer ",
+      "mä "
+    ],
+    "examples": [
+      "Beispiller"
+    ],
+    "feature": [
+      "Funktionalitéit"
+    ],
+    "given": [
+      "* ",
+      "ugeholl "
+    ],
+    "name": "Luxemburgish",
+    "native": "Lëtzebuergesch",
+    "scenario": [
+      "Szenario"
+    ],
+    "scenarioOutline": [
+      "Plang vum Szenario"
+    ],
+    "then": [
+      "* ",
+      "dann "
+    ],
+    "when": [
+      "* ",
+      "wann "
+    ]
+  },
+  "lv": {
+    "and": [
+      "* ",
+      "Un "
+    ],
+    "background": [
+      "Konteksts",
+      "Situācija"
+    ],
+    "but": [
+      "* ",
+      "Bet "
+    ],
+    "examples": [
+      "Piemēri",
+      "Paraugs"
+    ],
+    "feature": [
+      "Funkcionalitāte",
+      "Fīča"
+    ],
+    "given": [
+      "* ",
+      "Kad "
+    ],
+    "name": "Latvian",
+    "native": "latviešu",
+    "scenario": [
+      "Scenārijs"
+    ],
+    "scenarioOutline": [
+      "Scenārijs pēc parauga"
+    ],
+    "then": [
+      "* ",
+      "Tad "
+    ],
+    "when": [
+      "* ",
+      "Ja "
+    ]
+  },
+  "mn": {
+    "and": [
+      "* ",
+      "Мөн ",
+      "Тэгээд "
+    ],
+    "background": [
+      "Агуулга"
+    ],
+    "but": [
+      "* ",
+      "Гэхдээ ",
+      "Харин "
+    ],
+    "examples": [
+      "Тухайлбал"
+    ],
+    "feature": [
+      "Функц",
+      "Функционал"
+    ],
+    "given": [
+      "* ",
+      "Өгөгдсөн нь ",
+      "Анх "
+    ],
+    "name": "Mongolian",
+    "native": "монгол",
+    "scenario": [
+      "Сценар"
+    ],
+    "scenarioOutline": [
+      "Сценарын төлөвлөгөө"
+    ],
+    "then": [
+      "* ",
+      "Тэгэхэд ",
+      "Үүний дараа "
+    ],
+    "when": [
+      "* ",
+      "Хэрэв "
+    ]
+  },
+  "nl": {
+    "and": [
+      "* ",
+      "En "
+    ],
+    "background": [
+      "Achtergrond"
+    ],
+    "but": [
+      "* ",
+      "Maar "
+    ],
+    "examples": [
+      "Voorbeelden"
+    ],
+    "feature": [
+      "Functionaliteit"
+    ],
+    "given": [
+      "* ",
+      "Gegeven ",
+      "Stel "
+    ],
+    "name": "Dutch",
+    "native": "Nederlands",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Abstract Scenario"
+    ],
+    "then": [
+      "* ",
+      "Dan "
+    ],
+    "when": [
+      "* ",
+      "Als ",
+      "Wanneer "
+    ]
+  },
+  "no": {
+    "and": [
+      "* ",
+      "Og "
+    ],
+    "background": [
+      "Bakgrunn"
+    ],
+    "but": [
+      "* ",
+      "Men "
+    ],
+    "examples": [
+      "Eksempler"
+    ],
+    "feature": [
+      "Egenskap"
+    ],
+    "given": [
+      "* ",
+      "Gitt "
+    ],
+    "name": "Norwegian",
+    "native": "norsk",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Scenariomal",
+      "Abstrakt Scenario"
+    ],
+    "then": [
+      "* ",
+      "Så "
+    ],
+    "when": [
+      "* ",
+      "Når "
+    ]
+  },
+  "pa": {
+    "and": [
+      "* ",
+      "ਅਤੇ "
+    ],
+    "background": [
+      "ਪਿਛੋਕੜ"
+    ],
+    "but": [
+      "* ",
+      "ਪਰ "
+    ],
+    "examples": [
+      "ਉਦਾਹਰਨਾਂ"
+    ],
+    "feature": [
+      "ਖਾਸੀਅਤ",
+      "ਮੁਹਾਂਦਰਾ",
+      "ਨਕਸ਼ ਨੁਹਾਰ"
+    ],
+    "given": [
+      "* ",
+      "ਜੇਕਰ ",
+      "ਜਿਵੇਂ ਕਿ "
+    ],
+    "name": "Panjabi",
+    "native": "ਪੰਜਾਬੀ",
+    "scenario": [
+      "ਪਟਕਥਾ"
+    ],
+    "scenarioOutline": [
+      "ਪਟਕਥਾ ਢਾਂਚਾ",
+      "ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ"
+    ],
+    "then": [
+      "* ",
+      "ਤਦ "
+    ],
+    "when": [
+      "* ",
+      "ਜਦੋਂ "
+    ]
+  },
+  "pl": {
+    "and": [
+      "* ",
+      "Oraz ",
+      "I "
+    ],
+    "background": [
+      "Założenia"
+    ],
+    "but": [
+      "* ",
+      "Ale "
+    ],
+    "examples": [
+      "Przykłady"
+    ],
+    "feature": [
+      "Właściwość",
+      "Funkcja",
+      "Aspekt",
+      "Potrzeba biznesowa"
+    ],
+    "given": [
+      "* ",
+      "Zakładając ",
+      "Mając ",
+      "Zakładając, że "
+    ],
+    "name": "Polish",
+    "native": "polski",
+    "scenario": [
+      "Scenariusz"
+    ],
+    "scenarioOutline": [
+      "Szablon scenariusza"
+    ],
+    "then": [
+      "* ",
+      "Wtedy "
+    ],
+    "when": [
+      "* ",
+      "Jeżeli ",
+      "Jeśli ",
+      "Gdy ",
+      "Kiedy "
+    ]
+  },
+  "pt": {
+    "and": [
+      "* ",
+      "E "
+    ],
+    "background": [
+      "Contexto",
+      "Cenário de Fundo",
+      "Cenario de Fundo",
+      "Fundo"
+    ],
+    "but": [
+      "* ",
+      "Mas "
+    ],
+    "examples": [
+      "Exemplos",
+      "Cenários",
+      "Cenarios"
+    ],
+    "feature": [
+      "Funcionalidade",
+      "Característica",
+      "Caracteristica"
+    ],
+    "given": [
+      "* ",
+      "Dado ",
+      "Dada ",
+      "Dados ",
+      "Dadas "
+    ],
+    "name": "Portuguese",
+    "native": "português",
+    "scenario": [
+      "Cenário",
+      "Cenario"
+    ],
+    "scenarioOutline": [
+      "Esquema do Cenário",
+      "Esquema do Cenario",
+      "Delineação do Cenário",
+      "Delineacao do Cenario"
+    ],
+    "then": [
+      "* ",
+      "Então ",
+      "Entao "
+    ],
+    "when": [
+      "* ",
+      "Quando "
+    ]
+  },
+  "ro": {
+    "and": [
+      "* ",
+      "Si ",
+      "Și ",
+      "Şi "
+    ],
+    "background": [
+      "Context"
+    ],
+    "but": [
+      "* ",
+      "Dar "
+    ],
+    "examples": [
+      "Exemple"
+    ],
+    "feature": [
+      "Functionalitate",
+      "Funcționalitate",
+      "Funcţionalitate"
+    ],
+    "given": [
+      "* ",
+      "Date fiind ",
+      "Dat fiind ",
+      "Dati fiind ",
+      "Dați fiind ",
+      "Daţi fiind "
+    ],
+    "name": "Romanian",
+    "native": "română",
+    "scenario": [
+      "Scenariu"
+    ],
+    "scenarioOutline": [
+      "Structura scenariu",
+      "Structură scenariu"
+    ],
+    "then": [
+      "* ",
+      "Atunci "
+    ],
+    "when": [
+      "* ",
+      "Cand ",
+      "Când "
+    ]
+  },
+  "ru": {
+    "and": [
+      "* ",
+      "И ",
+      "К тому же ",
+      "Также "
+    ],
+    "background": [
+      "Предыстория",
+      "Контекст"
+    ],
+    "but": [
+      "* ",
+      "Но ",
+      "А "
+    ],
+    "examples": [
+      "Примеры"
+    ],
+    "feature": [
+      "Функция",
+      "Функциональность",
+      "Функционал",
+      "Свойство"
+    ],
+    "given": [
+      "* ",
+      "Допустим ",
+      "Дано ",
+      "Пусть "
+    ],
+    "name": "Russian",
+    "native": "русский",
+    "scenario": [
+      "Сценарий"
+    ],
+    "scenarioOutline": [
+      "Структура сценария"
+    ],
+    "then": [
+      "* ",
+      "То ",
+      "Затем ",
+      "Тогда "
+    ],
+    "when": [
+      "* ",
+      "Если ",
+      "Когда "
+    ]
+  },
+  "sk": {
+    "and": [
+      "* ",
+      "A ",
+      "A tiež ",
+      "A taktiež ",
+      "A zároveň "
+    ],
+    "background": [
+      "Pozadie"
+    ],
+    "but": [
+      "* ",
+      "Ale "
+    ],
+    "examples": [
+      "Príklady"
+    ],
+    "feature": [
+      "Požiadavka",
+      "Funkcia",
+      "Vlastnosť"
+    ],
+    "given": [
+      "* ",
+      "Pokiaľ ",
+      "Za predpokladu "
+    ],
+    "name": "Slovak",
+    "native": "Slovensky",
+    "scenario": [
+      "Scenár"
+    ],
+    "scenarioOutline": [
+      "Náčrt Scenáru",
+      "Náčrt Scenára",
+      "Osnova Scenára"
+    ],
+    "then": [
+      "* ",
+      "Tak ",
+      "Potom "
+    ],
+    "when": [
+      "* ",
+      "Keď ",
+      "Ak "
+    ]
+  },
+  "sl": {
+    "and": [
+      "In ",
+      "Ter "
+    ],
+    "background": [
+      "Kontekst",
+      "Osnova",
+      "Ozadje"
+    ],
+    "but": [
+      "Toda ",
+      "Ampak ",
+      "Vendar "
+    ],
+    "examples": [
+      "Primeri",
+      "Scenariji"
+    ],
+    "feature": [
+      "Funkcionalnost",
+      "Funkcija",
+      "Možnosti",
+      "Moznosti",
+      "Lastnost",
+      "Značilnost"
+    ],
+    "given": [
+      "Dano ",
+      "Podano ",
+      "Zaradi ",
+      "Privzeto "
+    ],
+    "name": "Slovenian",
+    "native": "Slovenski",
+    "scenario": [
+      "Scenarij",
+      "Primer"
+    ],
+    "scenarioOutline": [
+      "Struktura scenarija",
+      "Skica",
+      "Koncept",
+      "Oris scenarija",
+      "Osnutek"
+    ],
+    "then": [
+      "Nato ",
+      "Potem ",
+      "Takrat "
+    ],
+    "when": [
+      "Ko ",
+      "Ce ",
+      "Če ",
+      "Kadar "
+    ]
+  },
+  "sr-Cyrl": {
+    "and": [
+      "* ",
+      "И "
+    ],
+    "background": [
+      "Контекст",
+      "Основа",
+      "Позадина"
+    ],
+    "but": [
+      "* ",
+      "Али "
+    ],
+    "examples": [
+      "Примери",
+      "Сценарији"
+    ],
+    "feature": [
+      "Функционалност",
+      "Могућност",
+      "Особина"
+    ],
+    "given": [
+      "* ",
+      "За дато ",
+      "За дате ",
+      "За дати "
+    ],
+    "name": "Serbian",
+    "native": "Српски",
+    "scenario": [
+      "Сценарио",
+      "Пример"
+    ],
+    "scenarioOutline": [
+      "Структура сценарија",
+      "Скица",
+      "Концепт"
+    ],
+    "then": [
+      "* ",
+      "Онда "
+    ],
+    "when": [
+      "* ",
+      "Када ",
+      "Кад "
+    ]
+  },
+  "sr-Latn": {
+    "and": [
+      "* ",
+      "I "
+    ],
+    "background": [
+      "Kontekst",
+      "Osnova",
+      "Pozadina"
+    ],
+    "but": [
+      "* ",
+      "Ali "
+    ],
+    "examples": [
+      "Primeri",
+      "Scenariji"
+    ],
+    "feature": [
+      "Funkcionalnost",
+      "Mogućnost",
+      "Mogucnost",
+      "Osobina"
+    ],
+    "given": [
+      "* ",
+      "Za dato ",
+      "Za date ",
+      "Za dati "
+    ],
+    "name": "Serbian (Latin)",
+    "native": "Srpski (Latinica)",
+    "scenario": [
+      "Scenario",
+      "Primer"
+    ],
+    "scenarioOutline": [
+      "Struktura scenarija",
+      "Skica",
+      "Koncept"
+    ],
+    "then": [
+      "* ",
+      "Onda "
+    ],
+    "when": [
+      "* ",
+      "Kada ",
+      "Kad "
+    ]
+  },
+  "sv": {
+    "and": [
+      "* ",
+      "Och "
+    ],
+    "background": [
+      "Bakgrund"
+    ],
+    "but": [
+      "* ",
+      "Men "
+    ],
+    "examples": [
+      "Exempel"
+    ],
+    "feature": [
+      "Egenskap"
+    ],
+    "given": [
+      "* ",
+      "Givet "
+    ],
+    "name": "Swedish",
+    "native": "Svenska",
+    "scenario": [
+      "Scenario"
+    ],
+    "scenarioOutline": [
+      "Abstrakt Scenario",
+      "Scenariomall"
+    ],
+    "then": [
+      "* ",
+      "Så "
+    ],
+    "when": [
+      "* ",
+      "När "
+    ]
+  },
+  "ta": {
+    "and": [
+      "* ",
+      "மேலும்  ",
+      "மற்றும் "
+    ],
+    "background": [
+      "பின்னணி"
+    ],
+    "but": [
+      "* ",
+      "ஆனால்  "
+    ],
+    "examples": [
+      "எடுத்துக்காட்டுகள்",
+      "காட்சிகள்",
+      " நிலைமைகளில்"
+    ],
+    "feature": [
+      "அம்சம்",
+      "வணிக தேவை",
+      "திறன்"
+    ],
+    "given": [
+      "* ",
+      "கொடுக்கப்பட்ட "
+    ],
+    "name": "Tamil",
+    "native": "தமிழ்",
+    "scenario": [
+      "காட்சி"
+    ],
+    "scenarioOutline": [
+      "காட்சி சுருக்கம்",
+      "காட்சி வார்ப்புரு"
+    ],
+    "then": [
+      "* ",
+      "அப்பொழுது "
+    ],
+    "when": [
+      "* ",
+      "எப்போது "
+    ]
+  },
+  "th": {
+    "and": [
+      "* ",
+      "และ "
+    ],
+    "background": [
+      "แนวคิด"
+    ],
+    "but": [
+      "* ",
+      "แต่ "
+    ],
+    "examples": [
+      "ชุดของตัวอย่าง",
+      "ชุดของเหตุการณ์"
+    ],
+    "feature": [
+      "โครงหลัก",
+      "ความต้องการทางธุรกิจ",
+      "ความสามารถ"
+    ],
+    "given": [
+      "* ",
+      "กำหนดให้ "
+    ],
+    "name": "Thai",
+    "native": "ไทย",
+    "scenario": [
+      "เหตุการณ์"
+    ],
+    "scenarioOutline": [
+      "สรุปเหตุการณ์",
+      "โครงสร้างของเหตุการณ์"
+    ],
+    "then": [
+      "* ",
+      "ดังนั้น "
+    ],
+    "when": [
+      "* ",
+      "เมื่อ "
+    ]
+  },
+  "tl": {
+    "and": [
+      "* ",
+      "మరియు "
+    ],
+    "background": [
+      "నేపథ్యం"
+    ],
+    "but": [
+      "* ",
+      "కాని "
+    ],
+    "examples": [
+      "ఉదాహరణలు"
+    ],
+    "feature": [
+      "గుణము"
+    ],
+    "given": [
+      "* ",
+      "చెప్పబడినది "
+    ],
+    "name": "Telugu",
+    "native": "తెలుగు",
+    "scenario": [
+      "సన్నివేశం"
+    ],
+    "scenarioOutline": [
+      "కథనం"
+    ],
+    "then": [
+      "* ",
+      "అప్పుడు "
+    ],
+    "when": [
+      "* ",
+      "ఈ పరిస్థితిలో "
+    ]
+  },
+  "tlh": {
+    "and": [
+      "* ",
+      "'ej ",
+      "latlh "
+    ],
+    "background": [
+      "mo'"
+    ],
+    "but": [
+      "* ",
+      "'ach ",
+      "'a "
+    ],
+    "examples": [
+      "ghantoH",
+      "lutmey"
+    ],
+    "feature": [
+      "Qap",
+      "Qu'meH 'ut",
+      "perbogh",
+      "poQbogh malja'",
+      "laH"
+    ],
+    "given": [
+      "* ",
+      "ghu' noblu' ",
+      "DaH ghu' bejlu' "
+    ],
+    "name": "Klingon",
+    "native": "tlhIngan",
+    "scenario": [
+      "lut"
+    ],
+    "scenarioOutline": [
+      "lut chovnatlh"
+    ],
+    "then": [
+      "* ",
+      "vaj "
+    ],
+    "when": [
+      "* ",
+      "qaSDI' "
+    ]
+  },
+  "tr": {
+    "and": [
+      "* ",
+      "Ve "
+    ],
+    "background": [
+      "Geçmiş"
+    ],
+    "but": [
+      "* ",
+      "Fakat ",
+      "Ama "
+    ],
+    "examples": [
+      "Örnekler"
+    ],
+    "feature": [
+      "Özellik"
+    ],
+    "given": [
+      "* ",
+      "Diyelim ki "
+    ],
+    "name": "Turkish",
+    "native": "Türkçe",
+    "scenario": [
+      "Senaryo"
+    ],
+    "scenarioOutline": [
+      "Senaryo taslağı"
+    ],
+    "then": [
+      "* ",
+      "O zaman "
+    ],
+    "when": [
+      "* ",
+      "Eğer ki "
+    ]
+  },
+  "tt": {
+    "and": [
+      "* ",
+      "Һәм ",
+      "Вә "
+    ],
+    "background": [
+      "Кереш"
+    ],
+    "but": [
+      "* ",
+      "Ләкин ",
+      "Әмма "
+    ],
+    "examples": [
+      "Үрнәкләр",
+      "Мисаллар"
+    ],
+    "feature": [
+      "Мөмкинлек",
+      "Үзенчәлеклелек"
+    ],
+    "given": [
+      "* ",
+      "Әйтик "
+    ],
+    "name": "Tatar",
+    "native": "Татарча",
+    "scenario": [
+      "Сценарий"
+    ],
+    "scenarioOutline": [
+      "Сценарийның төзелеше"
+    ],
+    "then": [
+      "* ",
+      "Нәтиҗәдә "
+    ],
+    "when": [
+      "* ",
+      "Әгәр "
+    ]
+  },
+  "uk": {
+    "and": [
+      "* ",
+      "І ",
+      "А також ",
+      "Та "
+    ],
+    "background": [
+      "Передумова"
+    ],
+    "but": [
+      "* ",
+      "Але "
+    ],
+    "examples": [
+      "Приклади"
+    ],
+    "feature": [
+      "Функціонал"
+    ],
+    "given": [
+      "* ",
+      "Припустимо ",
+      "Припустимо, що ",
+      "Нехай ",
+      "Дано "
+    ],
+    "name": "Ukrainian",
+    "native": "Українська",
+    "scenario": [
+      "Сценарій"
+    ],
+    "scenarioOutline": [
+      "Структура сценарію"
+    ],
+    "then": [
+      "* ",
+      "То ",
+      "Тоді "
+    ],
+    "when": [
+      "* ",
+      "Якщо ",
+      "Коли "
+    ]
+  },
+  "ur": {
+    "and": [
+      "* ",
+      "اور "
+    ],
+    "background": [
+      "پس منظر"
+    ],
+    "but": [
+      "* ",
+      "لیکن "
+    ],
+    "examples": [
+      "مثالیں"
+    ],
+    "feature": [
+      "صلاحیت",
+      "کاروبار کی ضرورت",
+      "خصوصیت"
+    ],
+    "given": [
+      "* ",
+      "اگر ",
+      "بالفرض ",
+      "فرض کیا "
+    ],
+    "name": "Urdu",
+    "native": "اردو",
+    "scenario": [
+      "منظرنامہ"
+    ],
+    "scenarioOutline": [
+      "منظر نامے کا خاکہ"
+    ],
+    "then": [
+      "* ",
+      "پھر ",
+      "تب "
+    ],
+    "when": [
+      "* ",
+      "جب "
+    ]
+  },
+  "uz": {
+    "and": [
+      "* ",
+      "Ва "
+    ],
+    "background": [
+      "Тарих"
+    ],
+    "but": [
+      "* ",
+      "Лекин ",
+      "Бирок ",
+      "Аммо "
+    ],
+    "examples": [
+      "Мисоллар"
+    ],
+    "feature": [
+      "Функционал"
+    ],
+    "given": [
+      "* ",
+      "Агар "
+    ],
+    "name": "Uzbek",
+    "native": "Узбекча",
+    "scenario": [
+      "Сценарий"
+    ],
+    "scenarioOutline": [
+      "Сценарий структураси"
+    ],
+    "then": [
+      "* ",
+      "Унда "
+    ],
+    "when": [
+      "* ",
+      "Агар "
+    ]
+  },
+  "vi": {
+    "and": [
+      "* ",
+      "Và "
+    ],
+    "background": [
+      "Bối cảnh"
+    ],
+    "but": [
+      "* ",
+      "Nhưng "
+    ],
+    "examples": [
+      "Dữ liệu"
+    ],
+    "feature": [
+      "Tính năng"
+    ],
+    "given": [
+      "* ",
+      "Biết ",
+      "Cho "
+    ],
+    "name": "Vietnamese",
+    "native": "Tiếng Việt",
+    "scenario": [
+      "Tình huống",
+      "Kịch bản"
+    ],
+    "scenarioOutline": [
+      "Khung tình huống",
+      "Khung kịch bản"
+    ],
+    "then": [
+      "* ",
+      "Thì "
+    ],
+    "when": [
+      "* ",
+      "Khi "
+    ]
+  },
+  "zh-CN": {
+    "and": [
+      "* ",
+      "而且",
+      "并且",
+      "同时"
+    ],
+    "background": [
+      "背景"
+    ],
+    "but": [
+      "* ",
+      "但是"
+    ],
+    "examples": [
+      "例子"
+    ],
+    "feature": [
+      "功能"
+    ],
+    "given": [
+      "* ",
+      "假如",
+      "假设",
+      "假定"
+    ],
+    "name": "Chinese simplified",
+    "native": "简体中文",
+    "scenario": [
+      "场景",
+      "剧本"
+    ],
+    "scenarioOutline": [
+      "场景大纲",
+      "剧本大纲"
+    ],
+    "then": [
+      "* ",
+      "那么"
+    ],
+    "when": [
+      "* ",
+      "当"
+    ]
+  },
+  "zh-TW": {
+    "and": [
+      "* ",
+      "而且",
+      "並且",
+      "同時"
+    ],
+    "background": [
+      "背景"
+    ],
+    "but": [
+      "* ",
+      "但是"
+    ],
+    "examples": [
+      "例子"
+    ],
+    "feature": [
+      "功能"
+    ],
+    "given": [
+      "* ",
+      "假如",
+      "假設",
+      "假定"
+    ],
+    "name": "Chinese traditional",
+    "native": "繁體中文",
+    "scenario": [
+      "場景",
+      "劇本"
+    ],
+    "scenarioOutline": [
+      "場景大綱",
+      "劇本大綱"
+    ],
+    "then": [
+      "* ",
+      "那麼"
+    ],
+    "when": [
+      "* ",
+      "當"
+    ]
+  }
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDialectProviderTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDialectProviderTest.java
new file mode 100644
index 0000000..f7c737f
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDialectProviderTest.java
@@ -0,0 +1,168 @@
+/*
+ * SonarQube Gherkin Analyzer
+ * Copyright (C) 2016-2016 David RACODON
+ * david.racodon@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.gherkin.parser;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GherkinDialectProviderTest {
+
+  @Test
+  public void should_return_en_keywords() {
+    GherkinDialect dialect = GherkinDialectProvider.getDialect("en");
+
+    assertThat(dialect.getLanguage()).isEqualTo("en");
+
+    assertThat(dialect.getFeatureKeywords()).hasSize(3);
+    assertThat(dialect.getFeatureKeywords().contains("Feature")).isTrue();
+    assertThat(dialect.getFeatureKeywords().contains("Ability")).isTrue();
+    assertThat(dialect.getFeatureKeywords().contains("Business Need")).isTrue();
+
+    assertThat(dialect.getBackgroundKeywords()).hasSize(1);
+    assertThat(dialect.getBackgroundKeywords().contains("Background")).isTrue();
+
+    assertThat(dialect.getScenarioKeywords()).hasSize(1);
+    assertThat(dialect.getScenarioKeywords().contains("Scenario")).isTrue();
+
+    assertThat(dialect.getScenarioOutlineKeywords()).hasSize(2);
+    assertThat(dialect.getScenarioOutlineKeywords().contains("Scenario Outline")).isTrue();
+    assertThat(dialect.getScenarioOutlineKeywords().contains("Scenario Template")).isTrue();
+
+    assertThat(dialect.getExamplesKeywords()).hasSize(2);
+    assertThat(dialect.getExamplesKeywords().contains("Examples")).isTrue();
+    assertThat(dialect.getExamplesKeywords().contains("Scenarios")).isTrue();
+
+    assertThat(dialect.getStepKeywords()).hasSize(6);
+    assertThat(dialect.getStepKeywords().contains("Given ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("When ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Then ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("But ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("And ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("* ")).isTrue();
+
+    assertThat(dialect.getGivenStepKeywords()).hasSize(1);
+    assertThat(dialect.getGivenStepKeywords().contains("Given ")).isTrue();
+
+    assertThat(dialect.getWhenStepKeywords()).hasSize(1);
+    assertThat(dialect.getWhenStepKeywords().contains("When ")).isTrue();
+
+    assertThat(dialect.getThenStepKeywords()).hasSize(1);
+    assertThat(dialect.getThenStepKeywords().contains("Then ")).isTrue();
+
+    assertThat(dialect.getAndStepKeywords()).hasSize(1);
+    assertThat(dialect.getAndStepKeywords().contains("And ")).isTrue();
+
+    assertThat(dialect.getButStepKeywords()).hasSize(1);
+    assertThat(dialect.getButStepKeywords().contains("But ")).isTrue();
+  }
+
+  @Test
+  public void should_return_fr_keywords() {
+    GherkinDialect dialect = GherkinDialectProvider.getDialect("fr");
+
+    assertThat(dialect.getLanguage()).isEqualTo("fr");
+
+    assertThat(dialect.getFeatureKeywords()).hasSize(1);
+    assertThat(dialect.getFeatureKeywords().contains("Fonctionnalité")).isTrue();
+
+    assertThat(dialect.getBackgroundKeywords()).hasSize(1);
+    assertThat(dialect.getBackgroundKeywords().contains("Contexte")).isTrue();
+
+    assertThat(dialect.getScenarioKeywords()).hasSize(1);
+    assertThat(dialect.getScenarioKeywords().contains("Scénario")).isTrue();
+
+    assertThat(dialect.getScenarioOutlineKeywords()).hasSize(2);
+    assertThat(dialect.getScenarioOutlineKeywords().contains("Plan du scénario")).isTrue();
+    assertThat(dialect.getScenarioOutlineKeywords().contains("Plan du Scénario")).isTrue();
+
+    assertThat(dialect.getExamplesKeywords()).hasSize(1);
+    assertThat(dialect.getExamplesKeywords().contains("Exemples")).isTrue();
+
+    assertThat(dialect.getStepKeywords()).hasSize(24);
+    assertThat(dialect.getStepKeywords().contains("* ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Soit ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant donné que ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant donné qu'")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant donné ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant donnés ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant donnée ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Etant données ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant donné que ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant donné qu'")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant donné ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant donnée ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant donnés ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Étant données ")).isTrue();
+
+    assertThat(dialect.getStepKeywords().contains("Quand ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Lorsque ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Lorsqu'")).isTrue();
+
+    assertThat(dialect.getStepKeywords().contains("Alors ")).isTrue();
+
+    assertThat(dialect.getStepKeywords().contains("Mais ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Mais que ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Mais qu'")).isTrue();
+
+    assertThat(dialect.getStepKeywords().contains("Et ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Et que ")).isTrue();
+    assertThat(dialect.getStepKeywords().contains("Et qu'")).isTrue();
+
+    assertThat(dialect.getGivenStepKeywords()).hasSize(13);
+    assertThat(dialect.getGivenStepKeywords().contains("Soit ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant donné que ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant donné qu'")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant donné ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant donnés ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant donnée ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Etant données ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant donné que ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant donné qu'")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant donné ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant donnée ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant donnés ")).isTrue();
+    assertThat(dialect.getGivenStepKeywords().contains("Étant données ")).isTrue();
+
+    assertThat(dialect.getWhenStepKeywords()).hasSize(3);
+    assertThat(dialect.getWhenStepKeywords().contains("Quand ")).isTrue();
+    assertThat(dialect.getWhenStepKeywords().contains("Lorsque ")).isTrue();
+    assertThat(dialect.getWhenStepKeywords().contains("Lorsqu'")).isTrue();
+
+    assertThat(dialect.getThenStepKeywords()).hasSize(1);
+    assertThat(dialect.getThenStepKeywords().contains("Alors ")).isTrue();
+
+    assertThat(dialect.getButStepKeywords()).hasSize(3);
+    assertThat(dialect.getButStepKeywords().contains("Mais ")).isTrue();
+    assertThat(dialect.getButStepKeywords().contains("Mais que ")).isTrue();
+    assertThat(dialect.getButStepKeywords().contains("Mais qu'")).isTrue();
+
+    assertThat(dialect.getAndStepKeywords()).hasSize(3);
+    assertThat(dialect.getAndStepKeywords().contains("Et ")).isTrue();
+    assertThat(dialect.getAndStepKeywords().contains("Et que ")).isTrue();
+    assertThat(dialect.getAndStepKeywords().contains("Et qu'")).isTrue();
+  }
+
+  @Test(expected = IllegalStateException.class)
+  public void should_throw_an_exception_because_language_is_not_supported() {
+    GherkinDialectProvider.getDialect("blabla");
+  }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeFrTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeFrTest.java
new file mode 100644
index 0000000..0783bfe
--- /dev/null
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeFrTest.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube Gherkin Analyzer
+ * Copyright (C) 2016-2016 David RACODON
+ * david.racodon@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.gherkin.parser;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import org.junit.Test;
+import org.sonar.plugins.gherkin.api.tree.GherkinDocumentTree;
+
+import java.io.File;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class GherkinDocumentTreeFrTest extends GherkinTreeTest {
+
+  public GherkinDocumentTreeFrTest() {
+    super(GherkinLexicalGrammar.GHERKIN_DOCUMENT, "fr");
+  }
+
+  @Test
+  public void gherkinDocumentFr() throws Exception {
+    GherkinDocumentTree tree;
+
+    tree = checkParsed(new File("src/test/resources/parser/parse-fr.feature"));
+    assertThat(tree.hasByteOrderMark()).isEqualTo(false);
+    assertThat(tree.feature()).isNotNull();
+    assertThat(tree.feature().scenarioOutlines()).hasSize(1);
+    assertThat(tree.feature().scenarios()).hasSize(2);
+    assertThat(tree.feature().background()).isNotNull();
+    assertThat(tree.languageDeclaration()).isNotNull();
+    assertThat(tree.language()).isEqualTo("fr");
+  }
+
+  private GherkinDocumentTree checkParsed(File file) throws Exception {
+    GherkinDocumentTree tree = (GherkinDocumentTree) parser().parse(Files.toString(file, Charsets.UTF_8));
+    assertThat(tree).isNotNull();
+    assertThat(tree.languageDeclaration()).isNotNull();
+    assertThat(tree.language()).isNotNull();
+    assertThat(tree.language()).isEqualTo("fr");
+    return tree;
+  }
+
+}
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java
index c8c2eef..9d4a206 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinDocumentTreeTest.java
@@ -40,6 +40,8 @@ public void gherkinDocument() throws Exception {
 
     tree = checkParsed(new File("src/test/resources/parser/empty.feature"));
     assertThat(tree.feature()).isNull();
+    assertThat(tree.languageDeclaration()).isNull();
+    assertThat(tree.language()).isEqualTo("en");
 
     tree = checkParsed(new File("src/test/resources/parser/parse.feature"));
     assertThat(tree.hasByteOrderMark()).isEqualTo(false);
@@ -47,6 +49,8 @@ public void gherkinDocument() throws Exception {
     assertThat(tree.feature().scenarioOutlines()).hasSize(1);
     assertThat(tree.feature().scenarios()).hasSize(2);
     assertThat(tree.feature().background()).isNotNull();
+    assertThat(tree.languageDeclaration()).isNull();
+    assertThat(tree.language()).isEqualTo("en");
 
     tree = checkParsed(new File("src/test/resources/parser/parse-bom.feature"));
     assertThat(tree.hasByteOrderMark()).isEqualTo(true);
@@ -54,6 +58,8 @@ public void gherkinDocument() throws Exception {
     assertThat(tree.feature().scenarioOutlines()).hasSize(1);
     assertThat(tree.feature().scenarios()).hasSize(2);
     assertThat(tree.feature().background()).isNotNull();
+    assertThat(tree.languageDeclaration()).isNull();
+    assertThat(tree.language()).isEqualTo("en");
   }
 
   @Test
@@ -64,6 +70,7 @@ public void notGherkinDocument() throws Exception {
   private GherkinDocumentTree checkParsed(File file) throws Exception {
     GherkinDocumentTree tree = (GherkinDocumentTree) parser().parse(Files.toString(file, Charsets.UTF_8));
     assertThat(tree).isNotNull();
+    assertThat(tree.language()).isNotNull();
     return tree;
   }
 
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java
index 5ddad3c..d79a6d4 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinGrammarTest.java
@@ -28,7 +28,7 @@ public class GherkinGrammarTest {
   @Test(expected = RecognitionException.class)
   public void parse_error() throws Exception {
     GherkinParserBuilder
-      .createTestParser(Charsets.UTF_8, GherkinLexicalGrammar.GHERKIN_DOCUMENT)
+      .createTestParser(Charsets.UTF_8)
       .parse("blabla");
   }
 
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java
index 7e43fc5..d36206b 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/GherkinTreeTest.java
@@ -38,6 +38,10 @@ public GherkinTreeTest(GherkinLexicalGrammar ruleKey) {
     parser = GherkinParserBuilder.createTestParser(Charsets.UTF_8, ruleKey);
   }
 
+  public GherkinTreeTest(GherkinLexicalGrammar ruleKey, String language) {
+    parser = GherkinParserBuilder.createTestParser(Charsets.UTF_8, ruleKey, language);
+  }
+
   public ActionParser parser() {
     return parser;
   }
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java
index 8347083..d766e71 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepPrefixTreeTest.java
@@ -29,33 +29,38 @@ public StepPrefixTreeTest() {
 
   @Test
   public void stepPrefix() throws Exception {
-    checkParsed("Given", "Given");
-    checkParsed(" Given", "Given");
-    checkParsed("When", "When");
-    checkParsed(" When", "When");
-    checkParsed("Then", "Then");
-    checkParsed(" Then", "Then");
-    checkParsed("*", "*");
-    checkParsed(" *", "*");
-    checkParsed("And", "And");
-    checkParsed(" And", "And");
-    checkParsed("But", "But");
-    checkParsed(" But", "But");
+    checkParsed("Given ", "Given ");
+    checkParsed(" Given ", "Given ");
+    checkParsed("When ", "When ");
+    checkParsed(" When ", "When ");
+    checkParsed("Then ", "Then ");
+    checkParsed(" Then ", "Then ");
+    checkParsed("* ", "* ");
+    checkParsed(" * ", "* ");
+    checkParsed("And ", "And ");
+    checkParsed(" And ", "And ");
+    checkParsed("But ", "But ");
+    checkParsed(" But ", "But ");
   }
 
   @Test
   public void notStepPrefix() throws Exception {
-    checkNotParsed("given");
-    checkNotParsed("GIVEN");
-    checkNotParsed("when");
-    checkNotParsed("WHEN");
-    checkNotParsed("then");
-    checkNotParsed("THEN");
-    checkNotParsed("but");
-    checkNotParsed("BUT");
-    checkNotParsed("and");
-    checkNotParsed("AND");
-    checkNotParsed("blabla");
+    checkNotParsed("Given");
+    checkNotParsed("given ");
+    checkNotParsed("GIVEN ");
+    checkNotParsed("When");
+    checkNotParsed("when ");
+    checkNotParsed("WHEN ");
+    checkNotParsed("Then");
+    checkNotParsed("then ");
+    checkNotParsed("THEN ");
+    checkNotParsed("But");
+    checkNotParsed("but ");
+    checkNotParsed("BUT ");
+    checkNotParsed("And");
+    checkNotParsed("and ");
+    checkNotParsed("AND ");
+    checkNotParsed("blabla ");
   }
 
 }
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepSentenceTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepSentenceTreeTest.java
index 6d0eea9..97c564f 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepSentenceTreeTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepSentenceTreeTest.java
@@ -39,16 +39,6 @@ public void stepSentence() throws Exception {
     checkParsed("abc\\\\n def \n", "abc\\\\n def");
   }
 
-  @Test
-  public void notStepSentence() throws Exception {
-    checkNotParsed("Given abc\n");
-    checkNotParsed("When abc\n");
-    checkNotParsed("Then abc\n");
-    checkNotParsed("And abc\n");
-    checkNotParsed("But abc\n");
-    checkNotParsed("* abc\n");
-  }
-
   private void checkParsed(String toParse, String expected) {
     SyntaxToken token = (SyntaxToken) parser().parse(toParse);
     assertThat(token).isNotNull();
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java
index 4067ed5..3284390 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/parser/StepTreeTest.java
@@ -34,40 +34,42 @@ public StepTreeTest() {
   public void step() throws Exception {
     StepTree tree;
 
-    checkParsed("Given I am a customer", "Given", "I am a customer");
-    checkParsed(" Given I am a customer", "Given", "I am a customer");
-    checkParsed(" Given I am a customer ", "Given", "I am a customer ");
+    checkParsed("Given I am a customer", "Given ", "I am a customer");
+    checkParsed(" Given I am a customer", "Given ", "I am a customer");
+    checkParsed(" Given I am a customer ", "Given ", "I am a customer ");
 
-    checkParsed("When I add a product to my cart", "When", "I add a product to my cart");
-    checkParsed(" When I add a product to my cart", "When", "I add a product to my cart");
-    checkParsed(" When I add a product to my cart ", "When", "I add a product to my cart ");
+    checkParsed("When I add a product to my cart", "When ", "I add a product to my cart");
+    checkParsed(" When I add a product to my cart", "When ", "I add a product to my cart");
+    checkParsed(" When I add a product to my cart ", "When ", "I add a product to my cart ");
 
-    checkParsed("Then I should see the product in my cart", "Then", "I should see the product in my cart");
-    checkParsed(" Then I should see the product in my cart", "Then", "I should see the product in my cart");
-    checkParsed(" Then I should see the product in my cart ", "Then", "I should see the product in my cart ");
+    checkParsed("Then I should see the product in my cart", "Then ", "I should see the product in my cart");
+    checkParsed(" Then I should see the product in my cart", "Then ", "I should see the product in my cart");
+    checkParsed(" Then I should see the product in my cart ", "Then ", "I should see the product in my cart ");
 
-    checkParsed("* I should see the product in my cart", "*", "I should see the product in my cart");
-    checkParsed(" * I should see the product in my cart", "*", "I should see the product in my cart");
-    checkParsed(" * I should see the product in my cart ", "*", "I should see the product in my cart ");
+    checkParsed("* I should see the product in my cart", "* ", "I should see the product in my cart");
+    checkParsed(" * I should see the product in my cart", "* ", "I should see the product in my cart");
+    checkParsed(" * I should see the product in my cart ", "* ", "I should see the product in my cart ");
 
-    tree = checkParsed("Given I am a customer:\n\"\"\"\nblabla...\nblabla...\n\"\"\"", "Given", "I am a customer:");
+    tree = checkParsed("Given I am a customer:\n\"\"\"\nblabla...\nblabla...\n\"\"\"", "Given ", "I am a customer:");
     assertThat(tree.table()).isNull();
     assertThat(tree.docString()).isNotNull();
     assertThat(tree.docString().data()).hasSize(2);
 
-    tree = checkParsed("Given I am a customer:\n| abc |\n|2|", "Given", "I am a customer:");
+    tree = checkParsed("Given I am a customer:\n| abc |\n|2|", "Given ", "I am a customer:");
     assertThat(tree.table()).isNotNull();
     assertThat(tree.docString()).isNull();
     assertThat(tree.table().rows()).hasSize(2);
 
-    tree = checkParsed("Given a < b", "Given", "a < b");
+    tree = checkParsed("Given a < b", "Given ", "a < b");
     assertThat(tree.variables()).hasSize(0);
 
-    tree = checkParsed("Given blabla... zzz blabla...", "Given", "blabla... zzz blabla...");
+    tree = checkParsed("Given blabla... zzz blabla...", "Given ", "blabla... zzz blabla...");
     assertThat(tree.variables()).hasSize(2);
     assertThat(tree.variables().contains("abc")).isTrue();
     assertThat(tree.variables().contains("def")).isTrue();
     assertThat(tree.variables().contains("zzz")).isFalse();
+
+    checkParsed("Given  I am a customer", "Given ", " I am a customer");
   }
 
   @Test
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java
index 6aed816..f6b6600 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/SyntaxHighlighterVisitorTest.java
@@ -143,7 +143,7 @@ public void byte_order_mark() throws Exception {
 
   private void highlight(String string) throws Exception {
     inputFile.initMetadata(string);
-    Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(string);
+    Tree tree = GherkinParserBuilder.createTestParser(Charsets.UTF_8).parse(string);
     when(visitorContext.getTopTree()).thenReturn((GherkinDocumentTree) tree);
 
     Files.write(string, file, Charsets.UTF_8);
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java
index 09d5097..14c7cc8 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsTest.java
@@ -33,14 +33,14 @@ public class MetricsTest {
   @Test
   public void metrics_UTF8_file() {
     String path = "src/test/resources/metrics/metrics.feature";
-    Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(new File(path));
+    Tree tree = GherkinParserBuilder.createTestParser(Charsets.UTF_8).parse(new File(path));
     assertMetrics(tree);
   }
 
   @Test
   public void metrics_UTF8_file_with_BOM() {
     String path = "src/test/resources/metrics/metrics-bom.feature";
-    Tree tree = GherkinParserBuilder.createParser(Charsets.UTF_8).parse(new File(path));
+    Tree tree = GherkinParserBuilder.createTestParser(Charsets.UTF_8).parse(new File(path));
     assertMetrics(tree);
   }
 
diff --git a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java
index 761fecf..6554b26 100644
--- a/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java
+++ b/gherkin-frontend/src/test/java/org/sonar/gherkin/visitors/metrics/MetricsVisitorTest.java
@@ -53,7 +53,7 @@ public void test() {
 
     TreeVisitorContext treeVisitorContext = mock(TreeVisitorContext.class);
     when(treeVisitorContext.getFile()).thenReturn(inputFile.file());
-    when(treeVisitorContext.getTopTree()).thenReturn((GherkinDocumentTree) GherkinParserBuilder.createParser(Charsets.UTF_8).parse(inputFile.file()));
+    when(treeVisitorContext.getTopTree()).thenReturn((GherkinDocumentTree) GherkinParserBuilder.createTestParser(Charsets.UTF_8).parse(inputFile.file()));
 
     metricsVisitor.scanTree(treeVisitorContext);
 
diff --git a/gherkin-frontend/src/test/resources/parser/parse-fr.feature b/gherkin-frontend/src/test/resources/parser/parse-fr.feature
new file mode 100644
index 0000000..a45ac1f
--- /dev/null
+++ b/gherkin-frontend/src/test/resources/parser/parse-fr.feature
@@ -0,0 +1,32 @@
+# language: fr
+@mytag
+Fonctionnalité: My feature...
+  blaba...
+  blabla...
+
+  # blabla...
+  #blabla...
+
+  Contexte: :
+    Etant donné Blabla given...
+
+  Scénario: : Scenario 1
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
+
+  @tag
+  Plan du Scénario: Outline: Scenario 2
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
+
+    Exemples: :
+      | var |
+      | 1   |
+      | 2   |
+
+  Scénario: : Scenario 3
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
diff --git a/its/ruling/projects/custom/french.feature b/its/ruling/projects/custom/french.feature
new file mode 100644
index 0000000..283cefd
--- /dev/null
+++ b/its/ruling/projects/custom/french.feature
@@ -0,0 +1,16 @@
+# language: fr
+Fonctionnalité: My feature french
+  Blabla...
+
+  Scénario: My scenario 1 french
+    # Noncompliant [[sc=5;ec=7]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
+    * Blabla...
+    Lorsque Blabla when...
+    Alors Blabla then...
+
+  Scénario: My scenario 2 french
+    Soit Blabla given...
+    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
diff --git a/its/ruling/projects/custom/indentation/indentation-custom-ko.feature b/its/ruling/projects/custom/indentation/indentation-custom-ko.feature
index c593bc1..ca7c037 100644
--- a/its/ruling/projects/custom/indentation/indentation-custom-ko.feature
+++ b/its/ruling/projects/custom/indentation/indentation-custom-ko.feature
@@ -60,22 +60,19 @@
             | 2    |
 
 
-    # Noncompliant [[sc=16;ec=50]] {{Indent this token at column 15 (currently indented at column 16).}}
-    Scenario:  Scenario 3 - indentation custom KO
+    Scenario: Scenario 3 - indentation custom KO
         Blabla...
         Given Blabla given...
         When Blabla when
         Then Blabla then...
 
-    # Noncompliant [[sc=25;ec=60]] {{Indent this token at column 23 (currently indented at column 25).}}
-    Scenario Outline:   Scenario 4 - indentation custom KO
+    Scenario Outline: Scenario 4 - indentation custom KO
         Blabla...
         Given Blabla given...
         When Blabla when...
         Then Blabla then...
 
-        # Noncompliant [[sc=20;ec=58]] {{Indent this token at column 19 (currently indented at column 20).}}
-        Examples:  Blabla examples indentation custom KO
+        Examples: Blabla examples indentation custom KO
             | data |
             | 1    |
             | 2    |
diff --git a/its/ruling/projects/custom/indentation/indentation-default-ko.feature b/its/ruling/projects/custom/indentation/indentation-default-ko.feature
index 85dd1f5..3f9a296 100644
--- a/its/ruling/projects/custom/indentation/indentation-default-ko.feature
+++ b/its/ruling/projects/custom/indentation/indentation-default-ko.feature
@@ -2,7 +2,7 @@
  @tag
 @abc
   # Noncompliant [[sc=3;ec=10]] {{Indent this token at column 1 (currently indented at column 3).}}
-  Feature:  My feature indentation default KO
+  Feature: My feature indentation default KO
     # Noncompliant [[sc=5;ec=14]] {{Indent this token at column 3 (currently indented at column 5).}}
     Blabla...
   Blabla...
@@ -12,7 +12,7 @@
   # Noncompliant [[sc=3;ec=12]] {{Indent this token at column 5 (currently indented at column 3).}}
   Blabla...
     Blabla...
-    # Noncompliant [[sc=6;ec=11]] {{Indent this token at column 5 (currently indented at column 6).}}
+    # Noncompliant [[sc=6;ec=12]] {{Indent this token at column 5 (currently indented at column 6).}}
      Given Blabla given1...
 
     # Noncompliant [[sc=5;ec=6]] {{Indent this token at column 3 (currently indented at column 5).}}
@@ -60,22 +60,19 @@
        | 1    |
       | 2    |
 
-  # Noncompliant [[sc=14;ec=49]] {{Indent this token at column 13 (currently indented at column 14).}}
-  Scenario:  Scenario 3 - indentation default KO
+  Scenario: Scenario 3 - indentation default KO
     Blabla...
     Given Blabla given...
     When Blabla when
     Then Blabla then...
 
-  # Noncompliant [[sc=23;ec=58]] {{Indent this token at column 21 (currently indented at column 23).}}
-  Scenario Outline:   Scenario 4 - indentation default KO
+  Scenario Outline: Scenario 4 - indentation default KO
     Blabla...
     Given Blabla given...
     When Blabla when...
     Then Blabla then...
 
-    # Noncompliant [[sc=16;ec=54]] {{Indent this token at column 15 (currently indented at column 16).}}
-    Examples:  Blabla examples indentation default KO
+    Examples: Blabla examples indentation default KO
       | data |
       | 1    |
       | 2    |
diff --git a/its/ruling/projects/custom/indentation/indentation-single-whitespace.feature b/its/ruling/projects/custom/indentation/indentation-single-whitespace.feature
new file mode 100644
index 0000000..ed7de2b
--- /dev/null
+++ b/its/ruling/projects/custom/indentation/indentation-single-whitespace.feature
@@ -0,0 +1,40 @@
+# Noncompliant [[sc=11;ec=44;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+Feature:  My feature indentation default WS
+  Blabla...
+  Blabla...
+
+  # Noncompliant [[sc=16;ec=56;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Background:  Blabla background indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given blabla...
+
+  # Noncompliant [[sc=15;ec=48;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Scenario:   Scenario 1 indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given...
+    When Blabla when...
+    Then Blabla then...
+
+  # Noncompliant [[sc=22;ec=55;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+  Scenario Outline:  Scenario 2 indentation default WS
+    Blabla...
+    Blabla...
+    Given Blabla given...
+    When Blabla when...
+      | data |
+      | 2    |
+    Then Blabla then...
+      """string
+      Blabla...
+        Blabla...
+      """
+
+    # Noncompliant [[sc=16;ec=54;secondary=+0]] {{Leave one single whitespace between the name and the column.}}
+    Examples:  Blabla examples indentation default WS
+      Blabla...
+      Blabla...
+      | data |
+      | 1    |
+      | 2    |
diff --git a/its/ruling/projects/custom/spelling/spelling-fr.feature b/its/ruling/projects/custom/spelling/spelling-fr.feature
new file mode 100644
index 0000000..8eded51
--- /dev/null
+++ b/its/ruling/projects/custom/spelling/spelling-fr.feature
@@ -0,0 +1,7 @@
+# Noncompliant [[sc=27;ec=32]] {{[HUNSPELL_NO_SUGGEST_RULE] Faute de frappe possible trouvée.}}
+Feature: J'achète un joli veelo
+
+  Scenario: Blabla est le nom de mon scénario
+    Given Je suis sur le catalogue
+    When J'ajoute un joli vélo dans mon panier
+    Then Je devrais voir un joli vélo dans mon panier
diff --git a/its/ruling/projects/custom/star-step-prefix.feature b/its/ruling/projects/custom/star-step-prefix.feature
index 698a3a1..345bfc6 100644
--- a/its/ruling/projects/custom/star-step-prefix.feature
+++ b/its/ruling/projects/custom/star-step-prefix.feature
@@ -2,7 +2,7 @@ Feature: My feature Star step prefix
   Blabla...
 
   Scenario: My scenario 1 - Star step prefix
-    # Noncompliant [[sc=5;ec=6]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
+    # Noncompliant [[sc=5;ec=7]] {{Replace this star prefix with one of the Given/When/Then prefixes.}}
     * Blabla...
     When Blabla when...
     Then Blabla then...
diff --git a/its/ruling/projects/custom/step-of-unknown-type.feature b/its/ruling/projects/custom/step-of-unknown-type.feature
index 07702d1..47db042 100644
--- a/its/ruling/projects/custom/step-of-unknown-type.feature
+++ b/its/ruling/projects/custom/step-of-unknown-type.feature
@@ -2,7 +2,7 @@ Feature: My feature Step of unknown type
   Blabla...
 
   Scenario: Scenario 1 Step of unknown type
-    # Noncompliant [[sc=5;ec=8]] {{Update the prefix of this unknown type step.}}
+    # Noncompliant [[sc=5;ec=9]] {{Update the prefix of this unknown type step.}}
     And I am a customer
     When I add a product to my cart
     Then I should see the product in my cart
diff --git a/its/ruling/projects/custom/steps-right-order.feature b/its/ruling/projects/custom/steps-right-order.feature
index 05af2a5..1519869 100644
--- a/its/ruling/projects/custom/steps-right-order.feature
+++ b/its/ruling/projects/custom/steps-right-order.feature
@@ -5,13 +5,13 @@ Feature: My feature Steps right order
     Given I am a customer
     When I add a product to my cart
     Then I should see the product in my cart
-    # Noncompliant [[sc=5;ec=9]] {{Unexpected When step. Reorder the steps of this scenario.}}
+    # Noncompliant [[sc=5;ec=10]] {{Unexpected When step. Reorder the steps of this scenario.}}
     When I proceed to the order payment
     Then I should see the order total amount
 
   Scenario Outline: Scenario 2 Steps right order
     Given I am a customer
-    # Noncompliant [[sc=5;ec=9]] {{Unexpected Then step. Reorder the steps of this scenario.}}
+    # Noncompliant [[sc=5;ec=10]] {{Unexpected Then step. Reorder the steps of this scenario.}}
     Then I should see the product in my cart
     And I should see the order total amount 
     Examples:
diff --git a/its/ruling/projects/custom/use-and-but.feature b/its/ruling/projects/custom/use-and-but.feature
index 8cf9263..bb1ad99 100644
--- a/its/ruling/projects/custom/use-and-but.feature
+++ b/its/ruling/projects/custom/use-and-but.feature
@@ -3,14 +3,14 @@ Feature: My feature Use And But
 
   Scenario: Scenario 1 Use And But
     Given Blabla given1...
-    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=11]] {{Replace this redundant Given prefix with And or But prefix.}}
     Given Blabla given2...
     When Blabla when...
     Then Blabla then...
 
   Scenario Outline: Scenario 2 Use And But
     Given Blabla given1...
-    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant Given prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=11]] {{Replace this redundant Given prefix with And or But prefix.}}
     Given Blabla given2...
     When Blabla when...
     Then Blabla then...
@@ -23,7 +23,7 @@ Feature: My feature Use And But
     Given Blabla given...
     And Blabla...
     When Blabla when...
-    # Noncompliant [[sc=5;ec=9]] {{Replace this redundant When prefix with And or But.}}
+    # Noncompliant [[sc=5;ec=10]] {{Replace this redundant When prefix with And or But prefix.}}
     When Blabla when1...
     Then Blabla then...
 
diff --git a/its/ruling/tests/src/test/expected/gherkin-all-step-types-in-scenario.json b/its/ruling/tests/src/test/expected/gherkin-all-step-types-in-scenario.json
index 73000a4..8778c49 100644
--- a/its/ruling/tests/src/test/expected/gherkin-all-step-types-in-scenario.json
+++ b/its/ruling/tests/src/test/expected/gherkin-all-step-types-in-scenario.json
@@ -5,6 +5,9 @@
 29,
 34,
 ],
+'project:custom/french.feature':[
+5,
+],
 'project:custom/no-step/no-step.feature':[
 8,
 8,
diff --git a/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json b/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json
index a5ef700..0cdbd37 100644
--- a/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json
+++ b/its/ruling/tests/src/test/expected/gherkin-allowed-tags.json
@@ -13,25 +13,6 @@
 5,
 11,
 ],
-'project:custom/indentation/indentation-custom-ko.feature':[
-2,
-18,
-28,
-30,
-30,
-50,
-50,
-],
-'project:custom/indentation/indentation-custom-ok.feature':[
-1,
-2,
-12,
-20,
-20,
-34,
-35,
-35,
-],
 'project:custom/indentation/indentation-default-ko.feature':[
 2,
 3,
diff --git a/its/ruling/tests/src/test/expected/gherkin-duplicated-steps.json b/its/ruling/tests/src/test/expected/gherkin-duplicated-steps.json
index 155cc50..9953c50 100644
--- a/its/ruling/tests/src/test/expected/gherkin-duplicated-steps.json
+++ b/its/ruling/tests/src/test/expected/gherkin-duplicated-steps.json
@@ -11,4 +11,7 @@
 10,
 17,
 ],
+'project:custom/french.feature':[
+12,
+],
 }
diff --git a/its/ruling/tests/src/test/expected/gherkin-indentation.json b/its/ruling/tests/src/test/expected/gherkin-indentation.json
index 8a0fe0c..2ff7118 100644
--- a/its/ruling/tests/src/test/expected/gherkin-indentation.json
+++ b/its/ruling/tests/src/test/expected/gherkin-indentation.json
@@ -1,88 +1,4 @@
 {
-'project:custom/indentation/indentation-custom-ko.feature':[
-2,
-4,
-5,
-7,
-10,
-11,
-13,
-15,
-18,
-20,
-22,
-23,
-24,
-25,
-26,
-28,
-30,
-32,
-34,
-35,
-36,
-37,
-38,
-40,
-41,
-43,
-46,
-50,
-52,
-54,
-55,
-57,
-59,
-60,
-64,
-65,
-66,
-67,
-68,
-71,
-72,
-73,
-74,
-75,
-78,
-79,
-80,
-81,
-],
-'project:custom/indentation/indentation-custom-ok.feature':[
-4,
-5,
-7,
-8,
-9,
-10,
-12,
-13,
-14,
-15,
-16,
-17,
-18,
-20,
-21,
-22,
-23,
-24,
-25,
-26,
-27,
-28,
-29,
-32,
-34,
-35,
-36,
-37,
-38,
-39,
-40,
-41,
-],
 'project:custom/indentation/indentation-default-ko.feature':[
 2,
 5,
@@ -104,9 +20,13 @@
 56,
 58,
 60,
-64,
-71,
-78,
+],
+'project:custom/indentation/indentation-single-whitespace.feature':[
+2,
+7,
+13,
+21,
+35,
 ],
 'project:custom/tab-character/tab-character.feature':[
 5,
diff --git a/its/ruling/tests/src/test/expected/gherkin-no-tag-examples.json b/its/ruling/tests/src/test/expected/gherkin-no-tag-examples.json
index 43b9602..347d6fd 100644
--- a/its/ruling/tests/src/test/expected/gherkin-no-tag-examples.json
+++ b/its/ruling/tests/src/test/expected/gherkin-no-tag-examples.json
@@ -1,10 +1,4 @@
 {
-'project:custom/indentation/indentation-custom-ko.feature':[
-52,
-],
-'project:custom/indentation/indentation-custom-ok.feature':[
-36,
-],
 'project:custom/indentation/indentation-default-ko.feature':[
 53,
 ],
diff --git a/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json b/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json
index 650afd9..826818c 100644
--- a/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json
+++ b/its/ruling/tests/src/test/expected/gherkin-star-step-prefix.json
@@ -1,4 +1,7 @@
 {
+'project:custom/french.feature':[
+7,
+],
 'project:custom/star-step-prefix.feature':[
 6,
 ],
diff --git a/its/ruling/tests/src/test/expected/gherkin-step-of-unknown-type.json b/its/ruling/tests/src/test/expected/gherkin-step-of-unknown-type.json
index 8c24dcd..a29dc20 100644
--- a/its/ruling/tests/src/test/expected/gherkin-step-of-unknown-type.json
+++ b/its/ruling/tests/src/test/expected/gherkin-step-of-unknown-type.json
@@ -1,4 +1,7 @@
 {
+'project:custom/french.feature':[
+7,
+],
 'project:custom/star-step-prefix.feature':[
 6,
 ],
diff --git a/its/ruling/tests/src/test/expected/gherkin-use-and-but.json b/its/ruling/tests/src/test/expected/gherkin-use-and-but.json
index 800fdec..ba0c881 100644
--- a/its/ruling/tests/src/test/expected/gherkin-use-and-but.json
+++ b/its/ruling/tests/src/test/expected/gherkin-use-and-but.json
@@ -1,4 +1,7 @@
 {
+'project:custom/french.feature':[
+14,
+],
 'project:custom/one-single-when-per-scenario.feature':[
 19,
 ],
diff --git a/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java
index 51aed7d..bbae117 100644
--- a/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java
+++ b/its/ruling/tests/src/test/java/org/sonar/gherkin/its/GherkinRulingTest.java
@@ -63,7 +63,8 @@ public void test() throws Exception {
       .setProperty("dump.old", FileLocation.of("src/test/expected").getFile().getAbsolutePath())
       .setProperty("dump.new", FileLocation.of("target/actual").getFile().getAbsolutePath())
       .setProperty("lits.differences", litsDifferencesFile.getAbsolutePath())
-      .setProperty("sonar.cpd.skip", "true");
+      .setProperty("sonar.cpd.skip", "true")
+      .setProperty("sonar.exclusions", "custom/spelling/spelling-fr.feature,custom/indentation/indentation-custom-ok.feature,custom/indentation/indentation-custom-ko.feature");
     orchestrator.executeBuild(build);
 
     assertThat(Files.toString(litsDifferencesFile, StandardCharsets.UTF_8)).isEmpty();
diff --git a/pom.xml b/pom.xml
index e855bc0..eecd4b2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -180,7 +180,7 @@
       
       
         org.languagetool
-        language-en
+        language-all
         3.5
       
       
@@ -189,6 +189,11 @@
         1.2
         provided
       
+      
+        com.google.code.gson
+        gson
+        2.8.0
+      
       
         junit
         junit
diff --git a/sonar-gherkin-plugin/pom.xml b/sonar-gherkin-plugin/pom.xml
index 432bc45..b759cf3 100644
--- a/sonar-gherkin-plugin/pom.xml
+++ b/sonar-gherkin-plugin/pom.xml
@@ -68,28 +68,8 @@
             
             
               false
-              true
+              false
               false
-              
-                
-                  cglib:cglib-nodep
-                  
-                    **
-                  
-                
-                
-                  org.languagetool:language-en
-                  
-                    **
-                  
-                
-                
-                  org.languagetool:languagetool-core
-                  
-                    **
-                  
-                
-              
             
           
         
@@ -108,8 +88,8 @@
             
               
                 
-                  22000000
-                  21000000
+                  93000000
+                  92800000
                   
                     ${project.build.directory}/${project.build.finalName}.jar
                   
diff --git a/sonar-gherkin-plugin/src/main/java/org/sonar/plugins/gherkin/GherkinSquidSensor.java b/sonar-gherkin-plugin/src/main/java/org/sonar/plugins/gherkin/GherkinSquidSensor.java
index c28cbcd..e4a1309 100644
--- a/sonar-gherkin-plugin/src/main/java/org/sonar/plugins/gherkin/GherkinSquidSensor.java
+++ b/sonar-gherkin-plugin/src/main/java/org/sonar/plugins/gherkin/GherkinSquidSensor.java
@@ -37,6 +37,7 @@
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.gherkin.checks.ParsingErrorCheck;
+import org.sonar.gherkin.parser.GherkinDialectProvider;
 import org.sonar.gherkin.parser.GherkinParserBuilder;
 import org.sonar.gherkin.visitors.CharsetAwareVisitor;
 import org.sonar.gherkin.visitors.GherkinVisitorContext;
@@ -54,11 +55,11 @@
 import org.sonar.squidbridge.api.AnalysisException;
 
 import javax.annotation.Nullable;
-import java.io.File;
-import java.io.InterruptedIOException;
+import java.io.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
 
 public class GherkinSquidSensor implements Sensor {
 
@@ -66,7 +67,6 @@ public class GherkinSquidSensor implements Sensor {
 
   private final FileSystem fileSystem;
   private final GherkinChecks checks;
-  private final ActionParser parser;
   private final FilePredicate mainFilePredicate;
   private IssueSaver issueSaver;
   private RuleKey parsingErrorRuleKey = null;
@@ -82,8 +82,6 @@ public GherkinSquidSensor(FileSystem fileSystem, CheckFactory checkFactory, @Nul
       fileSystem.predicates().hasType(Type.MAIN),
       fileSystem.predicates().hasLanguage(GherkinLanguage.KEY));
 
-    this.parser = GherkinParserBuilder.createParser(fileSystem.encoding());
-
     this.checks = GherkinChecks.createGherkinChecks(checkFactory)
       .addChecks(GherkinRulesDefinition.REPOSITORY_KEY, GherkinRulesDefinition.getChecks())
       .addCustomChecks(customRulesDefinition);
@@ -128,6 +126,7 @@ public void execute(SensorContext sensorContext) {
 
   private List analyzeFile(SensorContext sensorContext, InputFile inputFile, List visitors) {
     try {
+      ActionParser parser = GherkinParserBuilder.createParser(fileSystem.encoding(), getFileLanguage(inputFile));
       GherkinDocumentTree gherkinDocument = (GherkinDocumentTree) parser.parse(new File(inputFile.absolutePath()));
       return scanFile(inputFile, gherkinDocument, visitors);
 
@@ -211,4 +210,17 @@ private static void checkInterrupted(Exception e) {
     }
   }
 
+  private String getFileLanguage(InputFile inputFile) {
+    try (BufferedReader br = new BufferedReader(new FileReader(inputFile.file()))) {
+      Matcher matcher = GherkinDialectProvider.LANGUAGE_DECLARATION_PATTERN.matcher(br.readLine());
+      if (matcher.find()) {
+        return matcher.group(1);
+      } else {
+        return GherkinDialectProvider.DEFAULT_LANGUAGE;
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException("Cannot determine language of file " + inputFile.absolutePath(), e);
+    }
+  }
+
 }
diff --git a/sonar-gherkin-plugin/src/test/java/org/sonar/plugins/gherkin/GherkinSquidSensorTest.java b/sonar-gherkin-plugin/src/test/java/org/sonar/plugins/gherkin/GherkinSquidSensorTest.java
index efc8402..a19f163 100644
--- a/sonar-gherkin-plugin/src/test/java/org/sonar/plugins/gherkin/GherkinSquidSensorTest.java
+++ b/sonar-gherkin-plugin/src/test/java/org/sonar/plugins/gherkin/GherkinSquidSensorTest.java
@@ -82,6 +82,30 @@ private void assertMeasure(String key) {
     assertThat(context.measure(key, CoreMetrics.CLASSES).value()).isEqualTo(1);
   }
 
+  @Test
+  public void should_execute_and_compute_valid_measures_on_UTF8_file_french() {
+    String relativePath = "my-feature-fr.feature";
+    inputFile(relativePath, Charsets.UTF_8);
+    createGherkinSquidSensor().execute(context);
+    assertMeasureFr("moduleKey:" + relativePath);
+  }
+
+  @Test
+  public void should_execute_and_compute_valid_measures_on_UTF8_with_BOM_file_french() {
+    String relativePath = "my-feature-bom-fr.feature";
+    inputFile(relativePath, Charsets.UTF_8);
+    createGherkinSquidSensor().execute(context);
+    assertMeasureFr("moduleKey:" + relativePath);
+  }
+
+  private void assertMeasureFr(String key) {
+    assertThat(context.measure(key, CoreMetrics.NCLOC).value()).isEqualTo(10);
+    assertThat(context.measure(key, CoreMetrics.STATEMENTS).value()).isEqualTo(6);
+    assertThat(context.measure(key, CoreMetrics.COMMENT_LINES).value()).isEqualTo(2);
+    assertThat(context.measure(key, CoreMetrics.FUNCTIONS).value()).isEqualTo(2);
+    assertThat(context.measure(key, CoreMetrics.CLASSES).value()).isEqualTo(1);
+  }
+
   @Test
   public void should_execute_and_save_issues_on_UTF8_with_BOM_file() {
     inputFile("my-feature-bom.feature", Charsets.UTF_8);
@@ -116,6 +140,40 @@ public void should_execute_and_save_issues_on_UTF8_file() {
     assertThat(context.allIssues()).hasSize(3);
   }
 
+  @Test
+  public void should_execute_and_save_issues_on_UTF8_with_BOM_file_french() {
+    inputFile("my-feature-bom-fr.feature", Charsets.UTF_8);
+
+    ActiveRules activeRules = (new ActiveRulesBuilder())
+      .create(RuleKey.of(GherkinRulesDefinition.REPOSITORY_KEY, CommentConventionCheck.class.getAnnotation(Rule.class).key()))
+      .activate()
+      .create(RuleKey.of(GherkinRulesDefinition.REPOSITORY_KEY, MissingNewlineAtEndOfFileCheck.class.getAnnotation(Rule.class).key()))
+      .activate()
+      .build();
+    checkFactory = new CheckFactory(activeRules);
+
+    createGherkinSquidSensor().execute(context);
+
+    assertThat(context.allIssues()).hasSize(3);
+  }
+
+  @Test
+  public void should_execute_and_save_issues_on_UTF8_file_french() {
+    inputFile("my-feature-fr.feature", Charsets.UTF_8);
+
+    ActiveRules activeRules = (new ActiveRulesBuilder())
+      .create(RuleKey.of(GherkinRulesDefinition.REPOSITORY_KEY, CommentConventionCheck.class.getAnnotation(Rule.class).key()))
+      .activate()
+      .create(RuleKey.of(GherkinRulesDefinition.REPOSITORY_KEY, MissingNewlineAtEndOfFileCheck.class.getAnnotation(Rule.class).key()))
+      .activate()
+      .build();
+    checkFactory = new CheckFactory(activeRules);
+
+    createGherkinSquidSensor().execute(context);
+
+    assertThat(context.allIssues()).hasSize(3);
+  }
+
   @Test
   public void should_raise_an_issue_because_the_parsing_error_rule_is_activated() {
     String relativePath = "parsing-error.feature";
diff --git a/sonar-gherkin-plugin/src/test/resources/my-feature-bom-fr.feature b/sonar-gherkin-plugin/src/test/resources/my-feature-bom-fr.feature
new file mode 100644
index 0000000..e22ae60
--- /dev/null
+++ b/sonar-gherkin-plugin/src/test/resources/my-feature-bom-fr.feature
@@ -0,0 +1,14 @@
+# language: fr
+Fonctionnalité: My feature...
+
+  #blabla...
+  #blabla...
+  Scénario: : Scenario 1
+    Soit : Blabla given...
+    Lorsqu'Blabla when...
+    Quand Blabla then...
+
+  Scénario: : Scenario 2
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
\ No newline at end of file
diff --git a/sonar-gherkin-plugin/src/test/resources/my-feature-fr.feature b/sonar-gherkin-plugin/src/test/resources/my-feature-fr.feature
new file mode 100644
index 0000000..8ffae16
--- /dev/null
+++ b/sonar-gherkin-plugin/src/test/resources/my-feature-fr.feature
@@ -0,0 +1,14 @@
+# language: fr
+Fonctionnalité: My feature...
+
+  #blabla...
+  #blabla...
+  Scénario: : Scenario 1
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
+
+  Scénario: : Scenario 2
+    Soit Blabla given...
+    Lorsque Blabla when...
+    Alors Blabla then...
\ No newline at end of file